안녕하세요. 개발 중인 정주입니다.
오늘은 "프로그래머스(Lv.2) - [3차] 방금그곡" 문제를 풀었습니다.
Github
문제 링크
풀이
이번 문제는 문자열 탐색 문제입니다. (아마?)
이 문제의 포인트는 #을 가진 멜로디를 구분하는 것과 조건을 잘 읽는 것입니다.
저는 이 두 개를 제대로 파악하지 못 해 여러 번 실패했습니다...
문제를 읽기 싫은 날이었는데 그게 제대로 돌아왔네요 ㅠㅠ
1. Input m을 멜로디 하나하나 나눠줍니다.
이번 문제는 #이 포함된 멜로디가 있습니다.
그래서 저처럼 생각 없이 한 글자씩 담으면 많이 속상한 결과가 나옵니다.
for문을 이용해 한 글자씩 확인하여 글자면 배열에 append, #이면 마지막 원소에 추가를 했습니다.
위 로직은 노래마다 사용을 해야하기 때문에 splitMelody() 메서드로 정의했습니다.
2. 노래 문자열 파싱하기
input musicinfos를 파싱해야 합니다.
쓰기 좋게 Album 구조체를 정의하였습니다.
struct Album {
var time: Int
var title: String
var music: [String]
}
문자열로 입력된 musicinfos을 ","를 기준으로 나눕니다.
2-1. time은 노래가 재생된 시간입니다. 입력된 끝난 시간 - 시작 시간을 계산하여 분 단위로 저장합니다.
let hour = Int(info[1].prefix(2))! - Int(info[0].prefix(2))!
let min = Int(info[1].suffix(2))! - Int(info[0].suffix(2))!
let time = hour * 60 + min
2-2. title은 input으로 들어온 그대로 넣었습니다.
2-3. music을 파싱합니다.
음악 길이보다 재생된 시간이 길 때는 음악이 끊김 없이 처음부터 반복해서 재생된다.
음악 길이보다 재생된 시간이 짧을 때는 처음부터 재생 시간만큼만 재생된다.
위 조건에 따라 구현을 하였습니다.
time이 멜로디 길이보다 짧을 경우 time 만큼 잘라서 저장합니다.
time이 멜로디 길이보다 같거나 길 경우 멜로디 전체 * (시간 / 멜로디 길이)를 저장합니다.
예를 들어 멜로디가 ABC 일 때 1분을 초과하면 ABCABC, 4분을 초과하면 ABCABCABC가 되는 것입니다.
3. 음악 배열을 재생 시간을 기준으로 정렬합니다.
위에서 만든 배열을 time을 기준으로 정렬합니다.
정렬을 하는 이유는 "조건이 일치하는 음악이 여러 개일 때에는 라디오에서 재생된 시간이 제일 긴 음악 제목을 반환한다." 라는 조건을 충족하기 위해서 입니다.
infos.sort { $0.time <= $1.time }
< 부호가 아닌 <= 부호를 사용한 이유는 "재생된 시간도 같을 경우 먼저 입력된 음악 제목을 반환한다." 라는 조건을 쉽게 구현하기 위해서 입니다.
아래에서 반복문을 이용해 동일한 멜로리를 찾을 텐데 나중에 나온 음악이 먼저 나와야 먼저 입력된 음악으로 덮어쓸 수 있습니다.
4. 멜로디가 포함된 노래를 찾습니다.
이제 정답을 찾아야 합니다.
inputMusic.count 는 입력된 멜로디 개수 입니다.
musicCount는 노래 배열의 노래 길이입니다.
4-1. 멜로디가 재생된 노래보다 길 경우 답이 아닙니다.
if musicCount < inputMusic.count {
continue
}
이 조건은 없어도 상관 없지만 직관성을 위해 추가하였습니다.
4-2. 멜로디가 포함된 노래를 찾습니다.
var count = 0
while musicCount >= count+inputMusic.count {
let ck = info.music[count..<(count+inputMusic.count)]
if m == ck.joined() {
result = info.title
break
}
count += 1
}
노래 배열을 멜로디 길이만큼 잘라서 확인합니다.
잘린 문자열이 입력 m과 같다면 result로 저장합니다.
만약 아니라면 한 칸씩 이동하여 다음 문자열을 확인합니다.
이 과정을 모든 노래에 걸쳐 체크합니다.
노래 배열은 재생 시간을 기준으로 오름차순, 등장한 역순으로 정렬되었기 때문에
잘린 문자열과 m 문자열이 같다면 바로 result로 갱신해도 괜찮습니다.
감사합니다!
전체 코드
import Foundation
struct Album {
var time: Int
var title: String
var music: [String]
}
func splitMelody(_ str: String) -> [String] {
var inputMusic: [String] = []
for ch in str {
if ch == "#" {
let t = inputMusic.popLast()!
inputMusic.append(t + "#")
} else {
inputMusic.append(String(ch))
}
}
return inputMusic
}
func solution(_ m:String, _ musicinfos:[String]) -> String {
var infos: [Album] = []
var inputMusic: [String] = splitMelody(m)
print("inputMusic: \(inputMusic)")
for musicinfo in musicinfos {
let info = musicinfo.components(separatedBy: ",")
let hour = Int(info[1].prefix(2))! - Int(info[0].prefix(2))!
let min = Int(info[1].suffix(2))! - Int(info[0].suffix(2))!
let time = hour * 60 + min
var melodies = splitMelody(info[3])
// print("melodies: \(melodies)")
var music: [String] = []
if time < melodies.count {
music = Array(melodies[0...time])
} else {
for i in 0..<Int(ceil(Double(time)/Double(melodies.count))) {
music += melodies
}
}
infos.append(Album(time: time, title: info[2], music: music))
}
infos.sort { $0.time <= $1.time }
var result = "(None)"
for info in infos {
let musicCount = info.music.count
print("info: \(info) / \(inputMusic.count) / \(musicCount)")
if musicCount < inputMusic.count {
continue
} else {
var count = 0
while musicCount >= count+inputMusic.count {
let ck = info.music[count..<(count+inputMusic.count)]
if m == ck.joined() {
result = info.title
break
}
count += 1
}
}
}
return result
}
아직은 초보 개발자입니다.
더 효율적인 코드 훈수 환영합니다!
공감과 댓글 부탁드립니다.