Algorithm/Programmers

Programmers Lv2) 방금그곡

삼쓰 웅쓰 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
}

 

배운점

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

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

화이팅!!!