2018 카카오 블라인드채용 3차문제로 나왔던 레벨2 문제이다.
네오가 들은 음과 재생정보가 주어질 때 네오가 들은 곡을 출력하는 문제이다.
기본적으로 문자열로 주어진 데이터를 다루는 문제인데 고려할 부분이 많아서 레벨 2치고 쉽지 않았던것같다.
단순하게 보면 주어진 곡 정보의 멜로디가 네오가 들은 멜로디 "m"과 같으면 답이겠거니 쉽게 생각할 수 있겠지만 몇가지 이 문제를 어렵게 만드는 문제가 있었다.
재생시간에 따라 멜로디가 끊길수도, 반복될 수도 있다.
재생시간이 너무 짧으면, "m"과 곡정보가 일치하더라도 정답이 아닐 수 있으며 반대도 성립한다.
예를들어 "m"이 "ABC"이고 곡정보 멜로디가 "ABCD"일때 일반적으로는 정답에 해당한다 생각할 수 있으나, 곡의 재생시간이 2초라면 실제 재생부분은 "AB"에서 끊어지기 때문에 정답후보에서 제외해야한다.
반대로 "m"이 "ABCD"이고 곡정보가 "CDAB"일때 정답이 아니라고 생각하기 쉬우나 곡 재생시간이 6초라면 한번 재생이 끝난 뒤 자동으로 처음부터 재생되어 실제 재생멜로디는 "CDABCD"가 되고 정답에 해당할 수 있는것이다.
따라서 재생시간에 따라 정보에 담긴 멜로디를 조정해줘야한다.
#의 존재..
곡 재생시간에 따라 멜로디를 잘라주는것 하나만 생각하면 어려운 일이 아니지만 여기서는 멜로디의 길이=재생시간이 성립하지 않는다. 왜냐하면 "#"이 붙은 음표는 문자열 길이로 따지면 2개이지만 1개의 음표로 1초에 재생이 되기때문이다. 따라서 "#"을 별도로 처리해줘야 곡재생시간에 맞춰서 멜로디를 딸수가 있다.
그렇다면 이 문제를 쉽게 만드는 방법은 간단해졌다. "#"이 붙은 음표를 한개의 문자로 식별 가능하게 변경하고, 곡별 멜로디를 실제 재생된 만큼만 표현하게하는것이다.
우선 #을 변환해보자
function sharp(str) { let arr = str.split('') for(let i=1;i<arr.length;i++){ //첫번째 글자는 #일수 없으므로 1번idx부터 if(arr[i]==='#'){ //해당 글자가 #이면 arr.splice(i,1) //그부분을 자르고 arr[i-1]=arr[i-1].toLowerCase() //직전 알파벳을 소문자로 } } return arr.join('') }
"#"이 등장하면"#"을 삭제하고 직전 알파벳을 소문자로 바꿔준다, 즉 "#"기호가 없어도 "C"와 "C#"등을 구분할 수 있게되었다.
그 다음은 곡별 정보를 가공해보자
각 정보는 [시작시간, 끝시간, 곡제목, 멜로디]로 되어있는데 이를 [곡제목, 재생시간만큼 표현되는 멜로디] 로 변경해줄 것이다.
function data (string){ // musicInfos의 각 string을 입력 let arr = string.split(',') // [0]=시작시간,[1]=끝시간,[2]=곡제목,[3]=멜로디 const time = (string) =>{ let [min,sec] = string.split(':').map(v=>+v) return min*60+sec //분:초 를 초단위로 변경해주기 } let playTime = time(arr[1])-time(arr[0]) //끝시간-시작시간 하면 재생시간이 나온다. let melody = sharp(arr[3]) //# 없애주기 return [arr[2],melody.repeat(Math.ceil(playTime/melody.length)).substr(0,playTime)] //arr[2]는 곡제목, "#"을 제거해준 멜로디를 필요한만큼 반복해주고 재생시간만큼 잘라준다. }
이제 원하는 함수를 구현했으니 해당 함수들을 이용하여 문제를 풀어준다.
이제 멜로디를 비교를 해서 정답을 내주면 되는데 또 조건이 있다. 정답에 해당하는 곡정보가 없으면 "(None)"을 출력하고, 답이 여러개가 있으면 재생시간이 더 긴곡을, 재생시간이 같으면 먼저 입력된 곡을 출력해야한다.
여러가지 방법이 있을 수 있겠다. 조건에 맞는 곡들을 배열에 넣고 마지막에 정렬해도 되고 현재까지 정답후보중 가장 재생시간이 긴것을 저장해도 되는데 배열이 편해서 배열을 이용해보도록 하겠다.
function solution(m, musicinfos) { let answer = [] //정답 후보가 여러개일 수 있으니 배열로 표현 const listen = sharp(m) //네오가 들은 멜로디 "#"제거 for(const info of musicinfos){ let played = data(info) //각 곡별로 played에는 [곡이름, 멜로디]가 들어가있음 if(played[1].includes(listen)){ //네오가 들은 멜로디가 곡멜로디에 포함되어있으면 answer.push(played) //정답에 played 저장 } } return answer.length==0?"(None)":answer.sort((a,b)=>b[1].length-a[1].length)[0][0] 정답 후보가 없으면 "(None)" 반환, 있으면 멜로디길이(재생시간)내림차순으로 정렬하여 첫번째곡이름 반환 }
결과는?
function sharp(str) { let arr = str.split('') for(let i=1;i<arr.length;i++){ if(arr[i]==='#'){ arr.splice(i,1) arr[i-1]=arr[i-1].toLowerCase() } } return arr.join('') } function data (string){ let arr = string.split(',') const time = (string) =>{ let [hour,min] = string.split(':').map(v=>+v) return hour*60+min } let playTime = time(arr[1])-time(arr[0]) let melody = sharp(arr[3]) return [arr[2],melody.repeat(Math.ceil(playTime/melody.length)).substr(0,playTime)] } function solution(m, musicinfos) { let answer =[] const listen = sharp(m) for(const info of musicinfos){ let played = data(info) if(played[1].includes(listen)){ answer.push(played) } } return answer.length==0?"(None)":answer.sort((a,b)=>b[2]-a[2])[0][0] }