코딩테스트

99클럽 코테 스터디 8일차 TIL : computeIfAbsent()

kchabin 2025. 1. 24. 02:22

입력

첫 번째 줄에 정환이 음을 아는 노래의 개수 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();
    }
}

 

좀 더 써야할 코드가 늘어나긴 하는데, 전체적인 로직은 크게 달라지지 않는다. 

확실히 코틀린이 편하게 쓸 수 있는 메서드가 많은 것 같다.