재생된 음악의 대한 정보가 순서대로 제공된다. 각 음악의 대한 정보는 문자열 형태로 들어오는 데 시작시간, 종료시간, 제목, 멜로디가 문자 ,를 기준으로 구분되어 주어진다. (03:00,03:08,TITLE,MELODY) Melody는 총 12개의 음으로 이루어지는 데 각각 C, C#, D, D#, E, F, F#, G, G#, A, A#, B 이다. 각각의 음정을 재생하는 데에는 1분이 걸리며 재생시간 동안 멜로디는 반복재생된다. 즉, 멜로디가 DGG#이고 7분 동안 재생되었으면 재생된 음악은 DGG#DGG#D이다.
내가 듣고 기억한 멜로디가 주어졌을 때 해당 멜로디를 포함된 음악을 찾으면 된다. 예를 들어 직전의 예시에서 G#DGG#을 들었으면 해당 음악은 내가 들은 멜로디를 포함한 것이다. 이 때 만족하는 음악이 여러개 있을 경우 지속 시간이 가장 긴 음악의 제목을 반환한다. 지속시간이 동일하다면 먼저 주어진 음악의 제목을 반환한다.
다만, 맞는 음악이 없으면 "(None)"을 반환한다.
우선 음악의 정보(시작 시간, 종료 시간, 총 재생된 멜로디, 주어진 순서)를 묶어서 생각한다. 이때 음정에서 #이 있으면 #을 제외하고 직전의 문자를 소문자로 만든다. 예를 들어 F#은 f으로 변환한다. 이는 이후에 문자열 매칭을 통해 확인하기 위해서이다. (만약 #을 제거하지 않으면 DGG#이라는 멜로디에는 DGG가 포함되게 된다.)
Filtering으로 통해 내가 들은 멜로디가 포함된 음악들을 찾아내고 그 중 최적의 음악을 찾아서 음악의 제목을 반환한다.
public String solution(String m, String[] musicInfos) {
Optional<Music> heardMusic = IntStream.range(0, musicInfos.length)
.mapToObj(index -> new Music(index, musicInfos[index]))
.filter(music -> music.playedIntervalsIncludes(m))
.max((preMusic, postMusic) -> {
if (preMusic.duration() == postMusic.duration())
return postMusic.index - preMusic.index;
else return (int) (preMusic.duration() - postMusic.duration());
});
return heardMusic.isPresent() ? heardMusic.get().title : "(None)";
}
class Music {
final int index;
final String title;
final LocalTime start, end;
final String playedIntervals;
public Music(int index, String info) {
this.index = index;
String[] infos = info.split(",");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
start = LocalTime.parse(infos[0], formatter);
end = LocalTime.parse(infos[1], formatter);
title = infos[2];
int playedTime = (int) (Duration.between(start, end).getSeconds() / 60);
String intervals = changeSharpToSingleInterval(infos[3]);
playedIntervals = intervals.repeat(playedTime / intervals.length())
+ intervals.substring(0, playedTime % intervals.length());
}
public boolean playedIntervalsIncludes(String intervals) {
String s = changeSharpToSingleInterval(intervals);
return playedIntervals.contains(s);
}
public long duration() {
return Duration.between(start, end).getSeconds();
}
private String changeSharpToSingleInterval(String intervals) {
StringBuilder changer = new StringBuilder(intervals);
for (int i = 0; i < changer.length(); i++) {
if (changer.charAt(i) == '#') {
changer.setCharAt(i - 1, Character.toLowerCase(changer.charAt(i - 1)));
changer.deleteCharAt(i--);
}
}
return changer.toString();
}
}