본문 바로가기
Xcode/Swift - Algorithm

Swift ) 프로그래머스(Lv2) - [3차] 방금 그곡 (String)

by 후르륵짭짭 2020. 9. 8.
728x90
반응형

programmers.co.kr/learn/courses/30/lessons/17683

 

코딩테스트 연습 - [3차] 방금그곡

방금그곡 라디오를 자주 듣는 네오는 라디오에서 방금 나왔던 음악이 무슨 음악인지 궁금해질 때가 많다. 그럴 때 네오는 다음 포털의 '방금그곡' 서비스를 이용하곤 한다. 방금그곡에서는 TV, ��

programmers.co.kr

안녕하세요 후르륵짭짭 입니다.

저는 이 문제를 보자마자,,, 아 쉽지 않겠다.

라는 생각이 빡! 들었고 역시 쉽지 않았습니다.

그리고 정말 많이 틀렸기 때문에,,,,, 30번 같은 문제를 왜 계속 틀릴지 너무 고생 많이 했습니다.

 

** 문제 해설 **

더보기
func solution(_ m:String, _ musicinfos:[String]) -> String {
    
    var answer : [(time: Int, name : String, index : Int)] = []
    
    //실제로 재생된 음악을 가져오자
    var musicList : [(start : String, end : String, name : String, rithym : String)] = []
    
    let newMusicinfos = musicinfos.map { (element) -> [String] in
        return element.components(separatedBy: ",")
    }
    
    
    for element in newMusicinfos {
    
            let start = element[0]
            let end = element[1]
            let name = element[2]
            let rithym = element[3]
            
            musicList.append((start, end, name, rithym))
 
    }
    
    
    //음을 나눠준다.
    let temp = Array(m).map { (char) -> String in
        return String(char)
    }
    
    let rememberMusic = makeMlist(temp)
    
    print(rememberMusic)
//    print(musicList)
    
    
    for element in musicList {
        
        
        let start = element.start.components(separatedBy: ":")
        let end = element.end.components(separatedBy: ":")
        let name = element.name
        let rithym = element.rithym.map { (char) -> String in
            return String(char)
        }
    
        //시간 계산
        var minute = (Int(end[0])! * 60 + Int(end[1])!) - (Int(start[0])! * 60 + Int(start[1])!)
//        print(minute , rithym.count)
        
        
        var originMusict = makeMlist(rithym)
            
        if(minute < 0) { minute *= -1 } // 23:00 - 00:00
        
        originMusict = makeMusic(minute, originMusict)
        
//        if minute - originMusict.count > 0 {
//            originMusict = makeMusic(minute, originMusict)
//        }
//        else{
//            originMusict = makeMusic(minute - originMusict.count, originMusict)
//        }

        print(originMusict)
        var flag = false
        for (index, single) in originMusict.enumerated() {
            
            if single == rememberMusic[0] && originMusict.count - index >= rememberMusic.count {
               
                var go = index
                
                for go2 in 0..<rememberMusic.count {
                    
                    if originMusict[go] != rememberMusic[go2] {
//                        print("go : \(go) \(originMusict[go]) \(rememberMusic[go2])")
                        break
                    }
                    
                    if go2 == rememberMusic.count - 1{
//                        print("YES")
                        
                        answer.append((minute, name, answer.count))
                        flag = true
                    }
                    
                    go += 1
                    
                }
                
            }
            
            if flag {
                break
            }
            
        }
        
        
    }
    
    answer = answer.sorted(by: { (element1, element2) -> Bool in
        return element1.time > element2.time

    })

    print(answer)
    
    return !answer.isEmpty ? answer.first!.name : "(None)"
    
}

func makeMusic(_ minute : Int ,_ origin : [String]) -> [String] {
    
    var newRithym : [String] = []
    
    let repeatCnt = minute / origin.count
    
    for _ in 0..<repeatCnt {
        newRithym += origin
    }
    
    
     newRithym += origin[0..<(minute % origin.count)]
    
    return newRithym
    
}


func makeMlist(_ temp : [String]) -> [String] {
 
    var mList : [String] = []
    
    var index = 0
    while index < temp.count {
        
        var twoWord = ""
        if index < temp.count - 1 {
        
            twoWord = temp[index] + temp[index + 1]
    
        }
        else{
            twoWord = temp[index]
        }
        
        if twoWord == "C#" || twoWord == "D#" || twoWord == "F#" || twoWord == "G#" || twoWord == "A#" {
            
            mList.append(twoWord)
            
            index += 2
        }
        else{
            
            mList.append(temp[index])
            index += 1
        }
        
        
    }
    
    return mList
    
}

하,,,,, 정말 코드 깁니다 ㅎㅎㅎㅎㅎㅎ

진짜로 깁니다 ㅎㅎㅎㅎ

//실제로 재생된 음악을 가져오자
    var musicList : [(start : String, end : String, name : String, rithym : String)] = []

let newMusicinfos = musicinfos.map { (element) -> [String] in
        return element.components(separatedBy: ",")
    }
    
    
    for element in newMusicinfos {
    
            let start = element[0]
            let end = element[1]
            let name = element[2]
            let rithym = element[3]
            
            musicList.append((start, end, name, rithym))
 
    }

일단 이렇게 각 문자를 ","를 기준으로 나눠줍니다. 그리고 musicList에 시작 끝 이름 악보를 저장해줍니다.

이제 악보를 바꿔줘야합니다. 

다른 사람들 코드를 보면 정말 다양한 방법을 사용했는데, 저는 특별한 기능이 생각 안나서, 직접 바꿔줬습니다.

처음에 두개를 묶고 그 문자가  "C#" ||  "D#" || "F#" ||  "G#" || "A#" 이라면 두칸 이동해주고 

그게 아니면 한칸만 뒤로 가게 해줘서 문자를 나눠줬습니다.

func makeMlist(_ temp : [String]) -> [String] {
 
    var mList : [String] = []
    
    var index = 0
    while index < temp.count {
        
        var twoWord = ""
        if index < temp.count - 1 {
        
            twoWord = temp[index] + temp[index + 1]
    
        }
        else{
            twoWord = temp[index]
        }
        
        if twoWord == "C#" || twoWord == "D#" || twoWord == "F#" || twoWord == "G#" || twoWord == "A#" {
            
            mList.append(twoWord)
            
            index += 2
        }
        else{
            
            mList.append(temp[index])
            index += 1
        }
        
        
    }
    
    return mList
    
}

이렇게 AA#BD#D 라면

AA를 묶고 위의 경우가 아니라면 A만 보내고 다시 A# 보고 위의 경우에 해당하니깐 BD를 보고,, 이렇게 나아갑니다.

func replaceSharp(music: String) -> String {
    var replacedMusic = music
    replacedMusic = replacedMusic.replacingOccurrences(of: "C#", with: "c")
    replacedMusic = replacedMusic.replacingOccurrences(of: "D#", with: "d")
    replacedMusic = replacedMusic.replacingOccurrences(of: "F#", with: "f")
    replacedMusic = replacedMusic.replacingOccurrences(of: "G#", with: "g")
    replacedMusic = replacedMusic.replacingOccurrences(of: "A#", with: "a")
    return replacedMusic
}

그런데 더 좋은 알고리즘이 있었습니다.

바로 String형 일 경우에, replacingOccurrences( A:B)를 사용하면 A를 B로 변경해줍니다.

정확한 알고리즘 원리를 알고 싶은데,, 알 수 있는 경로가 없는 것 같습니다.

 

이제 시작과 끝 시간을 알아야합니다.

/시간 계산
        var minute = (Int(end[0])! * 60 + Int(end[1])!) - (Int(start[0])! * 60 + Int(start[1])!)
        
        
        var originMusict = makeMlist(rithym)
            
        if(minute < 0) { minute *= -1 } // 23:00 - 00:00
        
        originMusict = makeMusic(minute, originMusict)

이렇게 시간을 구해주는데,,, 문제에 이렇게 되어 있어서 부족하면 건너 뛰는 줄 알았는데, 그게 아니였고

반대로, 한 음악을 중간에 끊을 경우 원본 음악에는 네오가 기억한 멜로디가 들어있다 해도 그 곡이 네오가 들은 곡이 아닐 수도 있다. 그렇기 때문에 네오는 기억한 멜로디를 재생 시간과 제공된 악보를 직접 보면서 비교하려고 한다. 

또 이 부분 때문에 너무 많은 시간을 고생 했습니다.

30번을 계속 틀린다면,,, 여기서 잘 못 될 가능성이 높습니다.

 if(minute < 0) { minute *= -1 } // 23:00 - 00:00

시간이 23시 부터 00 시가 되면 음수 값이 됩니다. 따라서 양수로 바꿔주는 작업을 해줘야합니다. 

저는 이 부분을 수정 해주니 계속 틀리던 30번이 정답 처리 되었습니다.

func makeMusic(_ minute : Int ,_ origin : [String]) -> [String] {
    
    var newRithym : [String] = []
    
    let repeatCnt = minute / origin.count
    
    for _ in 0..<repeatCnt {
        newRithym += origin
    }
    
    
     newRithym += origin[0..<(minute % origin.count)]
    
    return newRithym
    
}

이렇게 분이 주어지면 그 분을 1분 마다 재생되는 음의 개수로 나눠주고 위와 같은 방식으로 해결을 해줍니다.

 

 for (index, single) in originMusict.enumerated() {
            
            if single == rememberMusic[0] && originMusict.count - index >= rememberMusic.count {
               
                var go = index
                
                for go2 in 0..<rememberMusic.count {
                    
                    if originMusict[go] != rememberMusic[go2] {
//                        print("go : \(go) \(originMusict[go]) \(rememberMusic[go2])")
                        break
                    }
                    
                    if go2 == rememberMusic.count - 1{
//                        print("YES")
                        
                        answer.append((minute, name, answer.count))
                        flag = true
                    }
                    
                    go += 1
                    
                }
                
            }
            
            if flag {
                break
            }
            
        }

그리고 기억하는 음악이 ABCD 이고 재생된 음악이 BBBAB 라고 할때, 같은 A라면 탐색을 시작하게 되고 마지막 까지 동일하면 정답 처리 해줍니다. 그런데 만약 남은 음악이 기억하는 음악 보다 작다면 (AB < ABCD) 탐색을 안해줍니다.

 

 answer = answer.sorted(by: { (element1, element2) -> Bool in
        return element1.time > element2.time

    })
    
  return !answer.isEmpty ? answer.first!.name : "(None)"

마지막에, 정렬을 해주고 이렇게 첫번째 요소를 반환해주거나 존재하지 않다면 "(None)"을 해주면 정답이 됩니다!

 

개인적으로 구현하는데,,, 상당히 힘든 문제였습니다.... 따질 조건도 많았고,,, 그래도 어떻게든 해결해서 다행입니다.

다 맞은거 같은데, 틀린게 있다면 꼭 코드 부터 보도록 하겠습니다.

 

참고 사이트 

developer.apple.com/documentation/foundation/nsstring/1412937-replacingoccurrences

 

Apple Developer Documentation

 

developer.apple.com

 

728x90
반응형

댓글