프로그래머스_JS - 방금 그곡

nd098pkc·2022년 6월 25일
0

코딩테스트 준비

목록 보기
12/15

2018 카카오 블라인드채용 3차문제로 나왔던 레벨2 문제이다.

문제

네오가 들은 음과 재생정보가 주어질 때 네오가 들은 곡을 출력하는 문제이다.
기본적으로 문자열로 주어진 데이터를 다루는 문제인데 고려할 부분이 많아서 레벨 2치고 쉽지 않았던것같다.

풀이과정

<입력>

  1. 네오가 들은 멜로디 "m"(String)
  2. 라디오에서 재생된 곡별 정보가 담긴 "musicinfo"(Array)

<이 문제를 어렵게 만드는것>

단순하게 보면 주어진 곡 정보의 멜로디가 네오가 들은 멜로디 "m"과 같으면 답이겠거니 쉽게 생각할 수 있겠지만 몇가지 이 문제를 어렵게 만드는 문제가 있었다.

  1. 재생시간에 따라 멜로디가 끊길수도, 반복될 수도 있다.
    재생시간이 너무 짧으면, "m"과 곡정보가 일치하더라도 정답이 아닐 수 있으며 반대도 성립한다.

    예를들어 "m"이 "ABC"이고 곡정보 멜로디가 "ABCD"일때 일반적으로는 정답에 해당한다 생각할 수 있으나, 곡의 재생시간이 2초라면 실제 재생부분은 "AB"에서 끊어지기 때문에 정답후보에서 제외해야한다.
    반대로 "m"이 "ABCD"이고 곡정보가 "CDAB"일때 정답이 아니라고 생각하기 쉬우나 곡 재생시간이 6초라면 한번 재생이 끝난 뒤 자동으로 처음부터 재생되어 실제 재생멜로디는 "CDABCD"가 되고 정답에 해당할 수 있는것이다.

    따라서 재생시간에 따라 정보에 담긴 멜로디를 조정해줘야한다.

  2. #의 존재..
    곡 재생시간에 따라 멜로디를 잘라주는것 하나만 생각하면 어려운 일이 아니지만 여기서는 멜로디의 길이=재생시간이 성립하지 않는다. 왜냐하면 "#"이 붙은 음표는 문자열 길이로 따지면 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]
}
profile
늦게배운 코딩이 무섭다

0개의 댓글