[프로그래머스][LV.2] [3차] 방금그곡 | python3

2025. 2. 22. 15:27프로그래머스/LV.2

문제링크:  [3차] 방금그곡


문제설명

라디오를 자주 듣는 네오는 라디오에서 방금 나왔던 음악이 무슨 음악인지 궁금해질 때가 많다. 그럴 때 네오는 다음 포털의 '방금그곡' 서비스를 이용하곤 한다. 방금그곡에서는 TV, 라디오 등에서 나온 음악에 관해 제목 등의 정보를 제공하는 서비스이다.

네오는 자신이 기억한 멜로디를 가지고 방금그곡을 이용해 음악을 찾는다. 그런데 라디오 방송에서는 한 음악을 반복해서 재생할 때도 있어서 네오가 기억하고 있는 멜로디는 음악 끝부분과 처음 부분이 이어서 재생된 멜로디일 수도 있다. 반대로, 한 음악을 중간에 끊을 경우 원본 음악에는 네오가 기억한 멜로디가 들어있다 해도 그 곡이 네오가 들은 곡이 아닐 수도 있다. 그렇기 때문에 네오는 기억한 멜로디를 재생 시간과 제공된 악보를 직접 보면서 비교하려고 한다. 다음과 같은 가정을 할 때 네오가 찾으려는 음악의 제목을 구하여라.

 

- 방금그곡 서비스에서는 음악 제목, 재생이 시작되고 끝난 시각, 악보를 제공한다.
- 네오가 기억한 멜로디와 악보에 사용되는 음은 C, C#, D, D#, E, F, F#, G, G#, A, A#, B 12개이다.
- 각 음은 1분에 1개씩 재생된다. 음악은 반드시 처음부터 재생되며 음악 길이보다 재생된 시간이 길 때는 음악이 끊김 없이 처음부터 반복해서 재생된다. 음악 길이보다 재생된 시간이 짧을 때는 처음부터 재생 시간만큼만 재생된다.
- 음악이 00:00를 넘겨서까지 재생되는 일은 없다.
- 조건이 일치하는 음악이 여러 개일 때에는 라디오에서 재생된 시간이 제일 긴 음악 제목을 반환한다. 재생된 시간도 같을 경우 먼저 입력된 음악 제목을 반환한다.
- 조건이 일치하는 음악이 없을 때에는 “(None)”을 반환한다.

 

제한조건

입력 형식
입력으로 네오가 기억한 멜로디를 담은 문자열 m과 방송된 곡의 정보를 담고 있는 배열 musicinfos가 주어진다.

- m은 음 1개 이상 1439개 이하로 구성되어 있다.
- musicinfos는 100개 이하의 곡 정보를 담고 있는 배열로, 각각의 곡 정보는 음악이 시작한 시각, 끝난 시각, 음악 제목, 악보 정보가 ','로 구분된 문자열이다.
- 음악의 시작 시각과 끝난 시각은 24시간 HH:MM 형식이다.
- 음악 제목은 ',' 이외의 출력 가능한 문자로 표현된 길이 1 이상 64 이하의 문자열이다.
- 악보 정보는 음 1개 이상 1439개 이하로 구성되어 있다.


출력 형식
- 조건과 일치하는 음악 제목을 출력한다.


 

문제풀이

시간은 전부 분으로 바꾸어 준다. 다음에 나올 노래의 재생시간이 더 클때만 전체노래를 구하고 노래구절이 그 전체노래에 속하는지 비교한다. 

def solution(m, musicinfos):
    answer = ''
    maxm=0
    # newm=''
    # C#, D# 등을 하나로 표현하기 위해 소문자로 바꿈
    # for idx in range(len(m)):
    #     if m[idx]=='#':
    #         newm-=newm[-1]
    #         newm+=m[idx-1].lower()
    #     else:
    #         newm+=m[idx]
    
    # 생각해보니 replace로 해주면 되나.,?
    m=m.replace('C#','c')
    m=m.replace('D#','d')
    m=m.replace('A#','a')
    m=m.replace('F#','f')
    m=m.replace('G#','g')
    # 그냥 for문이 난 것 같기도,..
    
    for musicinfo in musicinfos:
        start,end,title,music=musicinfo.split(',')
        
        h=int(end[:2])-int(start[:2])
        mn=int(end[3:5])-int(start[3:5])
        
        mm=60*h+mn
        
        # if (h>maxh) or (h==maxh and m>maxm):
        # 분으로 바꿔서 풀면 고려할 것들이 더 적어질듯?
        
        if mm > maxm:
            music=music.replace('C#','c')
            music=music.replace('D#','d')
            music=music.replace('A#','a')
            music=music.replace('F#','f')
            music=music.replace('G#','g')
            # maxm=mm # 전체노래안에 구절이 있을때 업데이트 해줘야함
            # #부분을 고려해서 짜야함....
            totalmusic=music*((mm)//len(music))+music[:mm%len(music)]
            if m in totalmusic:
                maxm=mm
                answer=title
            # 대소문자 구별 하는데 왜..... # 리플레이스 쓰고 변수에 저장해줘야함
            # if 'c' in ['C']:
            #     print('대소분자구별안함')
            
            
    return answer

22,23,24,29,34에서 틀림....

반례를 찾아보니 m, info = "CDCDF", ["13:50,14:02,WORLD,CDCDCDF"] 이렇다고 한다..

 

그 전에 음악을 못찾을때를 안 넣어줌.. 이 코드부분을 추가하니 34 번에서만 틀렸다.

그리고 저 반례를 보니 시간 계산하는 부분을 수정해주면 될 것 같다. 먼저 시간을 분으로 바꾸어서 빼주는 식으로..

 

바꾸어줘도 34번에서 틀리네.... 시간이 문제가 아닌가?

 

아놔 어이없어.... B#도 추가해서 바꾸어줘야함. 문제에서 빼먹었나봐..

 

그래도 시간을 분으로 바꾸는 곳에서 더 나은 방법을 찾았으니 됐다.

나의코드

def solution(m, musicinfos):
    answer=''
    maxm=0
    # newm=''
    # C#, D# 등을 하나로 표현하기 위해 소문자로 바꿈
    # for idx in range(len(m)):
    #     if m[idx]=='#':
    #         newm-=newm[-1]
    #         newm+=m[idx-1].lower()
    #     else:
    #         newm+=m[idx]
    
    # 생각해보니 replace로 해주면 되나.,?
    m=m.replace('C#','c')
    m=m.replace('D#','d')
    m=m.replace('A#','a')
    m=m.replace('B#','b')
    m=m.replace('F#','f')
    m=m.replace('G#','g')
    # 그냥 for문이 난 것 같기도,..
    
    for musicinfo in musicinfos:
        start,end,title,music=musicinfo.split(',')
        
        # m, info = "CDCDF", ["13:50,14:02,WORLD,CDCDCDF"] 
        # 이런 경우를 대비해 분으로 바꾼다음 빼기
        
        startm=int(start[:2])*60+int(start[3:])
        endm=int(end[:2])*60+int(end[3:])
        
        mm=endm-startm
        
        # if (h>maxh) or (h==maxh and m>maxm):
        # 분으로 바꿔서 풀면 고려할 것들이 더 적어질듯?
        
        if mm > maxm:
            music=music.replace('C#','c')
            music=music.replace('D#','d')
            music=music.replace('A#','a')
            music=music.replace('B#','b')
            music=music.replace('F#','f')
            music=music.replace('G#','g')
            # maxm=mm # 전체노래안에 구절이 있을때 업데이트 해줘야함
            # #부분을 고려해서 짜야함....
            totalmusic=music*((mm)//len(music))+music[:mm%len(music)]
            if m in totalmusic:
                maxm=mm
                answer=title
            # 대소문자 구별 하는데 왜..... # 리플레이스 쓰고 변수에 저장해줘야함
            # if 'c' in ['C']:
            #     print('대소분자구별안함')
    # 음악을 못 찾을때를 안넣어줌..
    if not answer:
        return '(None)'
    return answer코드

 


 

더보기

참고하기

import io


def convert_sharp(music):
    music = music.replace('C#', 'c')
    music = music.replace('D#', 'd')
    music = music.replace('F#', 'f')
    music = music.replace('G#', 'g')
    music = music.replace('A#', 'a')
    return music


def solution(m, musicinfos):
    m = convert_sharp(m)
    longest_play_time = 0
    that_title = '(None)'
    for info in musicinfos:

        begin, end, title, music = info.split(',')
        music = convert_sharp(music)
        begin = int(begin[:2])*60 + int(begin[3:])
        end = int(end[:2])*60 + int(end[3:])
        length = end - begin
        if length < len(m):
            continue
        played_music = io.StringIO()
        for i in range(length):
            played_music.write(music[i % len(music)])
        played_music = played_music.getvalue()

        index = played_music.find(m)
        if index == -1:
            continue

        if longest_play_time < length:
            longest_play_time = length
            that_title = title

    return that_title

중복되는 부분은 함수화 시키기... 코테하면서 써보도록 하자

그리고 첨부터 answer='(None)"을 하면 나중에 조건문 안써도 됨.

 


📌 코드 분석

played_music = io.StringIO()  
for i in range(length):  
    played_music.write(music[i % len(music)])  
played_music = played_music.getvalue()

이 코드는 곡이 재생된 시간만큼 멜로디를 반복해서 문자열로 만드는 역할을 한다.
여기서 사용된 주요 함수는 io.StringIO(), .write(), .getvalue() 이다.


1️⃣ io.StringIO() (메모리 기반 문자열 저장소)

played_music = io.StringIO()
  • **io.StringIO()**는 문자열을 메모리에서 조작할 수 있는 객체를 생성하는 함수이다.
  • 일반적인 문자열(str)을 + 연산으로 연결하면 새로운 문자열 객체가 계속 생성되어 비효율적이다.
  • 하지만 StringIO를 사용하면 메모리에서 직접 문자열을 수정할 수 있어서 속도가 빠르다.
  • played_music은 StringIO 객체로, 이후에 .write()를 사용하여 문자열을 저장할 수 있다.

2️⃣ .write() (문자열 추가)

for i in range(length):  
    played_music.write(music[i % len(music)])
  • write() 함수는 StringIO 객체에 문자열을 추가하는 역할을 한다.
  • music[i % len(music)]은 재생 시간이 길 경우, 멜로디가 반복되도록 하기 위해 사용된다.

✔ 예제

music = "ABC"이고 length = 10이면:

i i % len(music) music[i % len(music)] played_music 내용
0 0 A A
1 1 B AB
2 2 C ABC
3 0 A ABCA
4 1 B ABCAB
5 2 C ABCABC
6 0 A ABCABCA
7 1 B ABCABCAB
8 2 C ABCABCABC
9 0 A ABCABCABCA

즉, write()를 사용해서 music을 length만큼 반복하여 StringIO에 저장한다.


3️⃣ .getvalue() (최종 문자열 가져오기)

played_music = played_music.getvalue()
  • StringIO 객체에 저장된 문자열을 일반 문자열(str)로 변환하는 함수이다.
  • getvalue()를 호출하면 played_music에 최종 재생된 멜로디 문자열이 저장된다.

📌 최종 정리

played_music = io.StringIO()  # 메모리에서 문자열을 저장할 객체 생성
for i in range(length):  
    played_music.write(music[i % len(music)])  # 재생된 멜로디를 저장
played_music = played_music.getvalue()  # 최종 문자열로 변환

io.StringIO() → 메모리에서 문자열을 효율적으로 저장할 수 있는 객체 생성
.write() → music을 length만큼 반복하여 StringIO에 추가
.getvalue() → StringIO 객체를 일반 문자열로 변환

이렇게 하면 재생된 시간 동안 반복된 음악 문자열을 만들 수 있다. 🎵


def shap_to_lower(s):
    s = s.replace('C#','c').replace('D#','d').replace('F#','f').replace('G#','g').replace('A#','a')
    return s

def solution(m,musicinfos):
    answer=[0,'(None)']   # time_len, title
    m = shap_to_lower(m)
    for info in musicinfos:
        split_info = info.split(',')
        time_length = (int(split_info[1][:2])-int(split_info[0][:2]))*60+int(split_info[1][-2:])-int(split_info[0][-2:])
        title = split_info[2]
        part_notes = shap_to_lower(split_info[-1])
        full_notes = part_notes*(time_length//len(part_notes))+part_notes[:time_length%len(part_notes)]
        if m in full_notes and time_length>answer[0]:
            answer=[time_length,title]
    return answer[-1]

리스트를 이용하여 푸는 방법. 그냥 [ , ] 이 구조로 계속 저장하여 씀