ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Programmers Lv2) 방금그곡
    Algorithm/Programmers 2020. 8. 28. 13:42
    728x90

    출처: programmers.co.kr/learn/courses/30/lessons/17683#qna

    분류: 카카오 블라인드 2018


    접근방식

    musicinfos를 이용해 정보를 가공하고 주어진 music이 포함되는지 확인하는 문제입니다.

    주의할 점은 # 이 포함되어 있는 음을 처리해주는 일인데요,
    단순히 "ABC" 가 포함되어 있는지 확인한다면 "ABC#" 인 경우를 걸러주지 못합니다.

    이를 체크하기 위해서는 대표적으로 두 가지 방법이 있습니다.

    1. 토큰화
      "ABC#" 를 ["A", "B", "C#"] 와 같이 의미있는 단위로 쪼개서 비교하는 방법입니다.
    2. 치환
      이 문제에서 의미있는 음표는 12개 뿐입니다. 따라서 "A#" 과 같은 음을 "a" 와 같이 의미없는 문자로 치환시켜서 해결해줍니다.

    저는 토근화를 시키면 배열끼리 비교하는 부분이 까다로울 것 같아서 치환으로 해결했습니다.

     

    해결방법

     

    Parsing

    1차적으로 musicinfo를 제가 원하는 정보 형태로 바꾸는 과정이 필요했습니다. 그리고 재생시간만큼 총 재생 된 음악을 만들어줬습니다.
    그리고 동시에 포함될 경우 재생시간이 더 긴 것을 선택해야 하기 때문에 재생시간으로 sort 까지 해줬습니다.

    let parsedInfos = musicinfos
            .map { info -> (Int, String, String) in
                let (playTime, title, music) = parseMusic(for: info)
                let replacedMusic = replaceSharp(music: music)
                let playedMusic = repeatMusic(playTime: playTime, music: replacedMusic)
                return (playTime, title, playedMusic)
            }
        .sorted { $0.0 > $1.0 }

     

    조금 더 구체적으로 살펴보겠습니다.

     

    "," 를 이용해서 쪼개고 재생 시간을 계산했습니다.
    재생 시간은 ":"로 쪼개고 분에 60을 곱해서 초 단위로 변경해줬습니다.

    func parseMusic(for info: String) -> (Int, String, String) {
        let parsed = info.split(separator: ",").map { String($0) }
        let playTime = parseToSecond(parsed[1]) - parseToSecond(parsed[0])
        return (playTime, parsed[2], parsed[3])
    }
    
    func parseToSecond(_ time: String) -> Int {
        let parsed = time.split(separator: ":").map { Int($0)! }
        return parsed[0] * 60 + parsed[1]
    }

     

    재생시간만큼 음악을 반복시켜줬는데요,
    재생시간을 음악의 길이로 나눴을 때 몫은 음악의 풀로 반복되어야 하고,
    나눈 나머지는 그만큼만 재생되어야 합니다.

    func repeatMusic(playTime: Int, music: String) -> String {
        var repeatedMusic = ""
        let musicTime = music.count
        repeatedMusic += String(repeating: music, count: (playTime / musicTime))
        repeatedMusic += music.dropLast(musicTime - (playTime % musicTime))
        return repeatedMusic
    }

     

    치환

    말씀드렸다시피 저는 # 음 처리에 치환을 사용했는데요, 더 스마트한 방법이 있을 것 같으나.. 그냥 replacingOccurrences 을 사용해서 일일히 변경해줬습니다 😅

    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이 포함되어 있는지 contains 메소드를 사용했고, 일치하는 음악이 없을 때는
    (None) 로 꼭 괄호를 포함해서 써주셔야 합니다!!! 

    이제 전체 코드를 보시면 이해되실 것 같습니다 :)

    import Foundation
    
    func solution(_ m:String, _ musicinfos:[String]) -> String {
        let m = replaceSharp(music: m)
        let parsedInfos = musicinfos
            .map { info -> (Int, String, String) in
                let (playTime, title, music) = parseMusic(for: info)
                let replacedMusic = replaceSharp(music: music)
                let playedMusic = repeatMusic(playTime: playTime, music: replacedMusic)
                return (playTime, title, playedMusic)
            }
        .sorted { $0.0 > $1.0 }
        
        for info in parsedInfos {
            let (_, title, playedMusic) = info
            if playedMusic.contains(m) {
                return title
            }
        }
        
        return "(None)"
    }
    
    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
    }
    
    func parseMusic(for info: String) -> (Int, String, String) {
        let parsed = info.split(separator: ",").map { String($0) }
        let playTime = parseToSecond(parsed[1]) - parseToSecond(parsed[0])
        return (playTime, parsed[2], parsed[3])
    }
    
    func parseToSecond(_ time: String) -> Int {
        let parsed = time.split(separator: ":").map { Int($0)! }
        return parsed[0] * 60 + parsed[1]
    }
    
    func repeatMusic(playTime: Int, music: String) -> String {
        var repeatedMusic = ""
        let musicTime = music.count
        repeatedMusic += String(repeating: music, count: (playTime / musicTime))
        repeatedMusic += music.dropLast(musicTime - (playTime % musicTime))
        return repeatedMusic
    }

     

    배운점

    # 음을 처리하는 부분에서 막혔었던 문제다. 

    치환과 토크나이즈.. 메모... ✍️

    화이팅!!!

    'Algorithm > Programmers' 카테고리의 다른 글

    Programmers Lv2) 파일명 정렬  (0) 2020.08.28
    Programmers Lv2) 압축  (0) 2020.08.28
    Programmers Lv2) 후보키  (0) 2020.08.25
    Programmers Lv2) 캐시  (0) 2020.08.24
    Programmers Lv3) 방문 길이  (0) 2020.07.10

    댓글

Designed by Tistory.