프로그래머스 2018 KAKAO BLIND RECRUITMENT Level 2 문제 방금그곡을 Java를 이용해 풀어보았다. 생각보다 오래 걸렸고.. 특정 테스트 케이스를 계속 통과하지 못해서 머리가 좀 아팠다. 테스트 케이스 25,26,28을 계속 통과하지 못했다. 어떤 이유에선지는 후술하겠다.
내가 푼 풀이는 정말 간단한 로직을 너무나도 길고 복잡하게 구현해놔서 올리기도 부끄럽지만.. 더 좋은 코드를 보며 성장하면 되지!
문제 링크 첨부한다.
https://programmers.co.kr/learn/courses/30/lessons/17683
C#CB
가 기본 악보이고 들은 멜로디 문자열 m
이 CBC
일 경우를 생각해보자. 6분 동안 재생됐다면 C#CBC#CB
가 재생됐을 것이다. 그러면 CBC
를 포함하고 있는지 확인하게 되면 있다고 판단한다. 하지만 실제 재생된 멜로디는 CBC
가 아닌 CBC#
이 재생된 것이다. 따라서 잘못된 로직으로 처리된다.
이를 해결하기 위해 #
이 붙은 음들을 모두 다른 문자로 치환해줘야 한다. 예를 들어 C#
은 c
로, D#
은 d
로 치환해주면 기본 악보가 cCB
가 될 것이다. 그러면 6분동안 재생된 악보는 cCBcCB
가 될 것이고 주어진 멜로디 CBC
를 포함하지 않기 때문에 정상적으로 처리된다.
이러한 입력값이 주어질 경우 제대로 처리하지 못해 테스트 케이스 25,26,28이 계속 실패
가 떴던 것이다.
이를 위해 다음과 같은 메소드를 선언했다.
static String transformMusic(String org){
org = org.replaceAll("C#", "c");
org = org.replaceAll("D#", "d");
org = org.replaceAll("E#", "e");
org = org.replaceAll("F#", "f");
org = org.replaceAll("G#", "g");
org = org.replaceAll("A#", "a");
return org;
}
기본 악보가 주어지면 재생되 시간만큼의 악보로 다시 만들어줘야 한다. 이를 위해 재생시간
과 기본 악보
를 param으로 넘겨줘서 실제 재생된 악보를 반환하면 된다.
이를 위해 다음과 같은 메소드를 선언했다.
static String createPlayedSheet(int playTime, String basicSheet){
basicSheet = transformMusic(basicSheet);
String res = "";
int idx = 0;
while(playTime--!=0){
res += ""+basicSheet.charAt(idx);
idx++;
if(idx==basicSheet.length()) idx = 0;
}
return res;
}
재생된 곡들은 다음과 같은 정보를 갖고있다.
- 재생된 순번
- 제목
- 재생 시간
- 실제 재생된 악보
위의 정보들을 갖춘 하나의 클래스를 선언해서 좀 더 보기 쉽게 작성했다. 코드는 아래와 같다.
static class Info{
int idx;
String title;
int playTime;
String playedSheet;
Info(int idx, String title, int playTime, String basicSheet){
this.idx = idx;
this.title = title;
this.playTime = playTime;
this.playedSheet = createPlayedSheet(playTime, basicSheet);
}
Integer getPlayTime(){ return this.playTime; }
Integer getIdx(){ return this.idx; }
}
앞서 봤던 createPlayedSheet
메소드를 여기서 생성자 코드 가운데 사용한 것을 확인할 수 있다.
그럼 주어진 musicinfos
배열을 돌며 재생된 하나의 곡에 대해 Info
객체를 생성해주며 List에 추가해주면 된다.
코드는 아래와 같다.
ArrayList<Info> list = new ArrayList<>();
int idx = 1;
for(String info: musicinfos){
String[] split = info.split(",");
Time begin = new Time(split[0]);
Time end = new Time(split[1]);
String title = split[2];
String basicSheet = split[3];
list.add(new Info(idx++, title, begin.getDiff(end), basicSheet));
}
그럼 이제 모든 재생된 곡들에 대한 Info
객체를 완성했기 때문에 실제로 네오가 들었던 멜로디가 포함된 객체들만 남겨두면 된다. 이 때는 Iterator
를 사용해서 해당 멜로디를 포함하지 않은 객체들은 날려버렸다.
Iterator<Info> itr = list.iterator();
while(itr.hasNext()){
Info cur = itr.next();
if(!cur.playedSheet.contains(m)) itr.remove();
}
if(list.size()==0) return "(None)";
만약 단 하나의 재생된 곡도 해당 멜로디를 포함하지 못했다면 List가 비었을테고 그럼 size
를 확인해서 0
일 경우에는 문제 조건대로 None
을 반환했다.
하지만 아직 해야할 작업이 남았다. 조건을 만족하는 여러 곡이 있다면 그 중 재생시간이 가장 긴 녀석들을 반환해야 하며, 재생시간이 같은 놈들이 있다면 그 중에서도 순번이 빠른 녀석들을 반환해줘야 한다.
이를 위해서는 Comparator
를 사용해서 sort
해줬다.
Info
클래스에서 재생시간과 순번을 반환하는 getter
메소드를 미리 선언해놔서 다음과 같이 정렬했다.
list.sort(Comparator.comparing(Info::getPlayTime).reversed().
thenComparing(Info::getIdx));
이렇게 정렬까지 마쳤으면 가장 앞에 있는 녀석의 타이틀을 반환해주면 된다.
String answer = list.get(0).title;
return answer;
다른 사람들의 풀이를 보면 훨씬 간단하고 압축적인 코드를 작성해놨다. 그런데 나는 사실 시험 시간에 시간 압박에 쫓기며 그렇게까지 응축되고 멋진 코드를 짜진 못할 것 같다. 그래서 나는 항상 객체지향적으로 코드를 짜서 구조화시키려 한다. 눈에 딱 보이는 구조로 짜지 않으면 나는 생각이 다 mix된다. 그래서 항상 필요한 정보들 중 서로 관련된 녀석들에 대해 클래스를 정의해서 다루려고 한다.
꼭 짧고 더 멋있고 간결한 코드로 짜는 것만이 코딩 테스트에서 능사는 아닌 것 같다.. 머리가 안 되는 나같은 놈들한테는...ㅋㅋㅋ