목표
- 기존에 푸는데만 집중했던 문제들을 클린 코드, 객체 지향 방식으로 리팩토링
- 완벽한 효율보다는 다양한 스타일로 작성
코테 문제) https://school.programmers.co.kr/learn/courses/30/lessons/17683
네오는 자신이 기억한 멜로디를 가지고 방금그곡을 이용해 음악을 찾는다. 그런데 라디오 방송에서는 한 음악을 반복해서 재생할 때도 있어서 네오가 기억하고 있는 멜로디는 음악 끝부분과 처음 부분이 이어서 재생된 멜로디일 수도 있다. 반대로, 한 음악을 중간에 끊을 경우 원본 음악에는 네오가 기억한 멜로디가 들어있다 해도 그 곡이 네오가 들은 곡이 아닐 수도 있다. 그렇기 때문에 네오는 기억한 멜로디를 재생 시간과 제공된 악보를 직접 보면서 비교하려고 한다. 다음과 같은 가정을 할 때 네오가 찾으려는 음악의 제목을 구하여라.
입력 형식
입력으로 네오가 기억한 멜로디를 담은 문자열 m과 방송된 곡의 정보를 담고 있는 배열 musicinfos가 주어진다.
def rep(s):
# '#'음정이 있는 음을 한 문자로 줄이기
d = {'C#':'c','D#':'d','F#':'f','G#':'g','A#':'a'}
# 딕셔너리를 돌며 모든 #문자를 축소
for k,v in d.items():
s = s.replace(k,v)
return s
def solution(m, musicinfos):
# 주어진 m 치환
m = rep(m)
# 제목 , 재생시간을 저장할 튜플
answer = ('',0)
for musicinfo in musicinfos:
# 음악 시작, 끝, 제목, 음 으로 나눈다.
s,e,title,music = musicinfo.split(',')
# 재생시간을 구해준다
run_time = int(e[:2])*60+int(e[3:]) - int(s[:2])*60 - int(s[3:])
# 음에서 '#'음정 치환
music = rep(music)
# 음악 길이가 재생시간보다 길어질 때 까지 늘려준 다음 재생시간만큼 자른다.
while len(music)<=run_time:
music*=2
music = music[:run_time]
# 음악 안에 찾는 멜로디 m이 없으면 넘어가고, 있으면 재생시간이 큰 쪽을 answer에 저장
if music.find(m)==-1:
continue
else:
answer = (title,run_time) if answer[1] < run_time else answer
# answer의 첫 원소가 초기화상태 그대로면, 없을 시 문구 리턴
if answer[0] =='':
return '(None)'
# 있다면 title 리턴
return answer[0]
class TimeUtils:
@staticmethod
def time_to_minutes(time: str) -> int:
hours, minutes = map(int, time.split(':'))
return hours * 60 + minutes
@staticmethod
def calculate_playtime(start: str, end: str) -> int:
return TimeUtils.time_to_minutes(end) - TimeUtils.time_to_minutes(start)
class Music:
def __init__(self, info: list[str]):
start, end, title, melody = info.split(',')
self.playtime = TimeUtils.calculate_playtime(start, end)
self.title = title
self.melody = melody
@property
def played_melody(self) -> str:
conv_melody: str = convert_sharp_to_lowercase(self.melody)
repeat: int = self.playtime // len(conv_melody) + 1
return (repeat * conv_melody)[:self.playtime]
class MusicFinder:
def __init__(self):
self.music_list = []
def append(self, music: Music) -> None:
self.music_list.append(music)
def find(self, melody: str) -> Music:
target_melody = convert_sharp_to_lowercase(melody)
found_music = None
max_playtime = 0
for music in self.music_list:
if (
target_melody in music.played_melody
and music.playtime > max_playtime
):
found_music = music
max_playtime = music.playtime
return found_music
def convert_sharp_to_lowercase(s: str) -> str:
SHARP = '#'
converted_melody = []
idx = len(s) - 1
while idx >= 0:
if s[idx] == SHARP:
converted_melody.append(s[idx - 1].lower())
idx -= 2
else:
converted_melody.append(s[idx])
idx -= 1
return ''.join(reversed(converted_melody))
def solution(m: str, musicinfos: list) -> str:
music_finder: MusicFinder = MusicFinder()
for music_info in musicinfos:
music_finder.append(Music(music_info))
found_music = music_finder.find(m)
return found_music.title if found_music else "(None)"
개선점
TimeUtils
, Music
, MusicFinder
객체를 통한 역할 분리#
이 나오면 문자 압축 (시간복잡도 O(N))