문제풀이/일일연습문제

[hash]99클럽 코테 스터디 7일차 TIL + 백준/Bronze/31562. 전주 듣고 노래 맞히기

Mo_bi!e 2024. 11. 4. 09:07

문제 설명

윤수와 정환은 「전주 듣고 노래 맞히기」라는 게임을 할 예정이다. 「전주 듣고 노래 맞히기」는 주어진 노래의 전주를 듣고 먼저 제목을 맞히는 사람이 점수를 얻어 최종적으로 점수가 더 많은 사람이 이기는 게임이다. 절대 음감을 가진 윤수는 노래의 첫 네 음만 듣고도 어떤 노래든 바로 맞힐 수 있다. 따라서, 정환은 윤수를 이기기 위해 첫 세 음만으로 노래를 맞히게 해주는 프로그램을 만들려고 한다. 우선 정환이 알고 있는 노래 제목, 음이름 등을 데이터로 만든 뒤 프로그램을 구현하기 시작했다. 예를 들어, 다음은 TwinkleStar(반짝반짝 작은 별)의 악보 중 일부이다.

 

 

 

위 악보를 박자와 관계없이 음이름으로 표현하면 CCGGAAG가 된다.

윤수를 이기기 위해서는 이 프로그램이 첫 세 음인 CCG만으로 노래 제목인 TwinkleStar를 출력할 수 있어야 한다. 또한, 세상의 모든 노래를 아는 윤수와 다르게 정환은 음을 아는 노래가 N개뿐이다. 그래서 프로그램에 N개의 노래의 정보를 저장해 놓을 것이다. 만약 저장된 노래 중 입력한 첫 세 음으로 시작하는 노래가 여러 개 있어 무슨 노래인지 정확히 알 수 없는 경우 ?를 출력하고, 입력한 첫 세 음에 맞는 저장된 노래가 없을 경우 !를 출력한다.

정환을 도와서 첫 세 음만으로 본인이 음을 아는 노래를 맞히는 프로그램을 완성하자. 이 프로그램은 대문자와 소문자를 구분한다.

 

- 입력

첫 번째 줄에 정환이 음을 아는 노래의 개수 N, 정환이 맞히기를 시도할 노래의 개수 M이 공백으로 구분되어 주어진다.

두 번째 줄부터 N개의 줄에 걸쳐 노래 제목의 길이 $T$, 영어 대소문자로 이루어진 문자열 노래 제목 S, 해당 노래에서 처음 등장하는 일곱 개의 음이름 a_1, a_2, a_3, a_4, a_5, a_6, a_7이 공백으로 구분되어 주어진다.

N+2번째 줄부터 M개의 줄에 걸쳐 정환이 맞히기를 시도할 노래의 첫 세 음의 음이름 b_1, b_2, b_3가 공백으로 구분되어 주어진다.

주어지는 음이름은 각각 C, D, E, F, G, A, B 중 하나이다. 같은 제목이 두 번 이상 주어지지 않는다.

 

- 출력

정환이 맞히기를 시도할 각 노래에 대하여 프로그램에 저장된 노래와 첫 세 음이 동일한 노래가 하나만 있다면 해당 노래의 제목을, 두 개 이상이면 ?을, 없다면 !을 한 줄에 하나씩 출력한다.

 

<내 코드>

import sys

# N, M 입력
N, M = sys.stdin.readline().split()
a_dict = {}
b_count = {}
count = 0
# N 입력
for i in range(int(N)):
    T, S, *a = sys.stdin.readline().split()
    a = "".join(a)[:3]

# list에 넣는방법
    if a in a_dict:
        a_dict[a].append(S)
    else :
        a_dict[a] = [S]


# M 입력
for j in range(int(M)):
    b_list = sys.stdin.readline().split()
    b = "".join(b_list)

    if b in a_dict:
        if len(a_dict[b]) > 1:
            print("?")
        else :
            print(a_dict[b][0])
    else :
        print("!")

1.  처음에는 리스트로 생각했다가 제목 출력을 위해 딕셔너리로 방식을 변경함

2. *asplit() 했을 때 디스트럭쳐링 방식이 있음을 알게됨

3. 필요한 부분만 슬라이싱으로 함

 

- 반성

1. 리스트 슬라이싱을 하면 같은 key 가 있을 수 있는데 이 부분에 대해서 딕셔너리 value 를 list로 넣는 방법을 익히게 됨

2. 딕셔너리의 장점에 대해서 생각하자 O(1) 로서 빠르게 검색이 가능

 

a = "".join(a)[:3] 부분이 a_key = "".join(a[:3])

이렇게도 가능함

 

 

 

<모범 사례>

import sys
from collections import defaultdict

input = sys.stdin.readline

# N, M 입력
N, M = map(int, input().split())
a_dict = defaultdict(list)

# N개의 노래 정보 입력
for _ in range(N):
    T, S, *a = input().split()
    a_key = "".join(a[:3])  # 첫 세 음으로 키 생성
    a_dict[a_key].append(S)

# M개의 시도 입력 및 결과 출력
results = []
for _ in range(M):
    b_key = "".join(input().split())
    
    # 첫 세 음에 맞는 노래를 찾는 조건 분기
    if b_key not in a_dict:
        results.append("!")  # 해당 키가 없으면 "!"
    elif len(a_dict[b_key]) == 1:
        results.append(a_dict[b_key][0])  # 유일한 노래가 있으면 제목 출력
    else:
        results.append("?")  # 여러 개의 노래가 있으면 "?"

# 한 번에 출력
sys.stdout.write("\n".join(results) + "\n")

거의 비슷함

 

 

<보충 학습>

1. 리스트를 value로 넣는 방법 익히기

 

2. defaultdict 이용하기

keyError 방지에 유용 -> key가 없을 때 자동으로 기본값 설정해

a_dict = {}
if 'key' not in a_dict:
    a_dict['key'] = []
a_dict['key'].append('value')

이 방식으로 if 문을 사용해서 []를 넣는대신에

 

from collections import defaultdict
a_dict = defaultdict(list)
a_dict['key'].append('value')  # 키가 없으면 자동으로 리스트 초기화 후 추가

list 로 한번에 넣기 가능

default 인자에다가 자료형을 넣으면 value가 자동으로 세팅이 됨