입력
첫 번째 줄에 정환이 음을 아는 노래의 개수 N$N$, 정환이 맞히기를 시도할 노래의 개수 M$M$이 공백으로 구분되어 주어진다.
두 번째 줄부터 N$N$개의 줄에 걸쳐 노래 제목의 길이 T$T$, 영어 대소문자로 이루어진 문자열 노래 제목 S$S$, 해당 노래에서 처음 등장하는 일곱 개의 음이름 a1,a2,a3,a4,a5,a6,a7$a_1, a_2, a_3, a_4, a_5, a_6, a_7$이 공백으로 구분되어 주어진다.
N+2$N+2$번째 줄부터 M$M$개의 줄에 걸쳐 정환이 맞히기를 시도할 노래의 첫 세 음의 음이름 b1,b2,b3$b_1, b_2, b_3$가 공백으로 구분되어 주어진다.
주어지는 음이름은 각각 C, D, E, F, G, A, B 중 하나이다. 같은 제목이 두 번 이상 주어지지 않는다.
출력
정환이 맞히기를 시도할 각 노래에 대하여 프로그램에 저장된 노래와 첫 세 음이 동일한 노래가 하나만 있다면 해당 노래의 제목을, 두 개 이상이면 ?을, 없다면 !을 한 줄에 하나씩 출력한다.
제한
- $1 \le N \le 1\,000$
- 1≤N≤1000
- $1 \le M \le 1\,000$
- 1≤M≤1000
- $1 \le T \le 30$
- 1≤T≤30
- 입력으로 주어지는 모든 수는 정수이다.
예제 입력 1 복사
4 4
11 TwinkleStar C C G G A A G
8 Marigold E D E F E E D
23 DoYouWannaBuildASnowMan C C C G C E D
12 Cprogramming C C C C C C C
E D E
C G G
C C C
C C G
예제 출력 1 복사
Marigold
!
?
TwinkleStar
내 풀이
처음 입력받는 M, N을 반복에 사용해야 한다.
M번 반복하면서 아는 노래 제목과 코드를 저장하고, N번 반복하면서 코드에 따라 노래를 맞혀야 한다.
fun main() {
//N, M 입력받기
val (N, M) = readLine()!!.split(" ").map{ it.toInt() }
//3개의 음이 key, value는 노래제목(복수 가능)
val songMap = mutableMapOf<List<String>, MutableList<String>>()
//N번에 걸쳐서 아는 노래 입력받기
repeat(N){
val line = readLine()!!.split(" ")
val title = line[1]
val notes = line.subList(2, 9) //마지막 인덱스 제외. 7개의 음 입력받기
val key = notes.take(3) //처음 3개의 음
//처음 입력받는 key이면 빈 리스트 생성, 이미 있는 key이면 리스트에 title 추가
// "C", "C", "G" -> [ title1, title2 ]
songMap.computeIfAbsent(key) { mutableListOf() }.add(title)
}
var results = mutableListOf<String>()
//M번에 걸쳐 세 음만 듣고 노래 맞추기
repeat(M){
//세 음 입력받기
val song = readLine()!!.split(" ")
val key = song.take(3)
when(val titles = songMap[key]){
null -> results.add("!")
else -> {
results.add(
if(titles.size==1) titles[0]
else "?"
)
}
}
}
println(results.joinToString(" "))
}
- listOf(”C”, “C”, “G”), [ “TwinkleStar”, “MariGold” ] 같은 형태로 맵에 키값을 저장한다.
- 키는 3개의 음, 값은 제목이다.
- subList를 쓰면 파이썬의 slice처럼 일정 인덱스 범위만큼 하위 리스트를 추출할 수 있다.
- take를 쓰면 리스트에서 처음 n개를 가져올 수 있다.
- computeIfAbsent(key)는 key가 처음 받는 값이면 빈 리스트를 만든 뒤 리스트에 title을 추가한다. 이미 있는 key이면 title만 리스트에 추가한다.
- 세 음 입력받는 것도 비슷하게 take를 사용한다.
- when을 사용하면 switch-case 문을 좀 더 간결하게 사용할 수 있다. songMap[key], 즉 곡 제목으로 이루어진 리스트의 값을 검증해서 답변 리스트에 저장한다.
- 없는 제목이면 느낌표, 하나만 있는 제목이면 해당 제목을, 두 개 이상이면 ?을 저장한다.
- 마지막엔 문자열 리스트 값을 문자열로 바꿔서 출력한다.
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// N, M 입력받기
int N = scanner.nextInt();
int M = scanner.nextInt();
scanner.nextLine(); // 개행문자 처리
// 3개의 음이 key, value는 노래 제목 리스트
Map<List<String>, List<String>> songMap = new HashMap<>();
// N번에 걸쳐서 아는 노래 입력받기
for (int i = 0; i < N; i++) {
String[] line = scanner.nextLine().split(" ");
String title = line[1];
List<String> notes = Arrays.asList(Arrays.copyOfRange(line, 2, 9)); // 7개의 음
List<String> key = notes.subList(0, 3); // 처음 3개의 음
// key에 대한 값이 없으면 새 리스트 생성 후 추가
songMap.computeIfAbsent(key, k -> new ArrayList<>()).add(title);
}
List<String> results = new ArrayList<>();
// M번에 걸쳐 세 음만 듣고 노래 맞추기
for (int i = 0; i < M; i++) {
String[] song = scanner.nextLine().split(" ");
List<String> key = Arrays.asList(song[0], song[1], song[2]);
List<String> titles = songMap.get(key);
if (titles == null) {
results.add("!");
} else if (titles.size() == 1) {
results.add(titles.get(0));
} else {
results.add("?");
}
}
// 결과 출력
results.forEach(System.out::println);
scanner.close();
}
}
좀 더 써야할 코드가 늘어나긴 하는데, 전체적인 로직은 크게 달라지지 않는다.
확실히 코틀린이 편하게 쓸 수 있는 메서드가 많은 것 같다.
'코딩테스트' 카테고리의 다른 글
99클럽 코테 스터디 7일차 TIL : HashSet (0) | 2025.01.22 |
---|---|
99클럽 코테 스터디 6일차 TIL : Hashing (0) | 2025.01.22 |
99클럽 코테 스터디 4일차 TIL : reverse() (0) | 2025.01.16 |
99클럽 코테 스터디 3일차 TIL : StringBuilder (0) | 2025.01.16 |
99클럽 코테 스터디 2일차 TIL : Scanner와 BufferedReader (0) | 2025.01.15 |