본문 바로가기
알고리즘

프로그래머스 베스트앨범 (python, 파이썬)

by 블쭌 2020. 10. 24.
728x90
  • 문제

스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.

  1. 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
  2. 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
  3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다

  • 입출력

노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.

제한사항

  • genres[i]는 고유번호가 i인 노래의 장르입니다.
  • plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
  • genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
  • 장르 종류는 100개 미만입니다.
  • 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
  • 모든 장르는 재생된 횟수가 다릅니다.

  • 코드
def solution(genres, plays):
    answer = []
	
    dic = {x:[] for x in set(genres)}

    for x in zip(genres, plays, range(len(genres))):
        dic[x[0]].append([x[1], x[2]])

    sort_genre = sorted(list(dic.keys()), key=lambda x: sum(map(lambda y: y[0], dic[x])), reverse=True)

    for i in sort_genre:
        temp = [x[1] for x in sorted(dic[i], key=lambda x: (x[0], -x[1]), reverse=True)]
        answer.extend(temp[:min(len(temp), 2)])

    return answer

python의 중고급 skill의 함수들이 많이 들어가있어서 한줄한줄 설명해보겠습니다.

genres와 plays의 list는 다음과 같다고 가정하고 설명하겠습니다.

 

genres = ['classic', 'pop', 'classic', 'classic', 'pop']
plays = [500, 600, 150, 800, 2500]

 

dic = {x:[] for x in set(genres)}
print(dic)
# {'classic': [], 'pop': []}

set함수를 통해 중복된 장르를 제거하고 unique한 장르만 남긴다.

먼저 list comprehension에 대한 이해가 필요하다.

위는 set(genres)에 대한 값(['classic', 'pop'])을 x로 받고(여기서 말하는 x는 for x in set(genres) 부분)

최종 {} (dictionary를 의미)안에 담기는 것은 앞에서 받은 x를 key로하고 value에 [](빈 리스트)를 만드는 것이다.

 

for x in zip(genres, plays, range(len(genres))):
	dic[x[0]].append([x[1], x[2]]) 
print(dic)
# {'pop': [[600, 1], [2500, 4]], 'classic': [[500, 0], [150, 2], [800, 3]]}

zip함수는 말그대로 묶어주는 함수이다. zip함수를 모르면 for문을 반복적으로 쓰거나 index로 접근하거나 좀 더 코드가 복잡해질수 있는 것을 간단하게 만들어준다.

genres 리스트, plays 리스트, range(len(genres))에 대한 값을 index 순차적으로 3개의 list를 한꺼번에 접근이 가능하다.

즉 x로받는 첫번째 값은 ['classic', 500, 0] 두번쨰 값은 ['pop', 600, 1]이다.

마지막 range(len(genres))를 한 이유는 해당 노래에대한 고유번호를 만들기 위함이다.

 

dic는 현재 key가 장르이기때문에 x[0](장르)로 접근해서 value인 빈리스트에 [x[1](재생수),x[2](고유번호)]를 묶어서 append시켜준다.

 

sort_genre = sorted(list(dic.keys()), key=lambda x: sum(map(lambda y: y[0], dic[x])), reverse=True)
print(sort_genre)
# ['pop', 'classic']

 

여기서는 장르별 우선으로 한다고했기 때문에 재생수가 더 높은 장르 순서로 정렬하는 것이다.

sorted(정렬하고 싶은 값들, key= lambda x: x는 앞에 정렬하고 싶은 값들이 오는데 원하는 기준을 여기에 지정해주면 된다.)

현재 dic안의 key값들이 장르이기 때문에 해당 값을 맨 처음에 적어주고(list(dic.keys())

현재 x값은 장르이기 때문에 dic[x]를 접근해서 재생수를 불러와야하기 때문에 map 함수를 다시 이용해준다.

map(lambda y : y[0], dic[x]) : x는 장르이기때문에 해당장르의 value값 [재생수, 고유번호]에서 y[0](즉 재생수)를 map함수를 통해 각각 불러온다. 예를들어서 'pop': [[600, 1], [2500, 4]]의 경우 600, 2500을 map함수를 통해 불러오는 것이다.

불러온 재생수들을 sum함수를 통해 합을 구해주면 된다.

마지막 reverse=True는 내림차순 정렬을 위해 사용해준다.

 

    for i in sort_genre:
        temp = [x[1] for x in sorted(dic[i], key=lambda x: (x[0], -x[1]), reverse=True)]
        print(temp)
		# [4, 1]    -> 'pop'
		# [3, 0, 2] -> 'classic'
        answer.extend(temp[:min(len(temp), 2)])
        
        

이제 재생수가 높은 장르를 찾았기 때문에 이번엔 각 장르에서 재생수가 높은 순서 재생수가 같다면 고유번호가 낮은 순서를 찾으면 된다.

위에서 찾은 장르를 통해 각 장르를 불러오고(sorted(dic[i]) 재생수가 높은 순서 (x[0]) 왜 0인지는 위에서 보면 dic의 key값을 장르로 접근하면 value중에서 첫번째 값이 재생수이기 때문이다. 그 다음에 lambda x: 뒤에 (1,2)는 1을 기준으로 정렬하고 1의 값이 같으면 2의 기준으로 정렬하라는 뜻이다. x[1]은 고유번호이기 때문에 고유번호 기준으로로 정렬하는데 고유번호는 낮은순서로 뽑아야하기 때문에 앞에 -를 붙여준다. reverse=True를 뺀다면 (-x[0], x[1])로 해주면 된다.

 

이제 2개의 값만 뽑아야 하는데 장르별로 1개의 곡이 존재할수도 있기 때문에 min함수를 통해 현재길이와 2를 비교하여 낮은 값을 찾아서 index slicing을 해주면 된다.

 

마지막으로 append를 하게되면 [[4,1], [0,3]]이렇게 되버린다. 즉 list에 list를 append했기 때문이다. 하나의 list처럼 보이게하려면 extend함수를 쓰면된다.


  • 출처

programmers.co.kr/learn/courses/30/parts/12077

 

728x90

댓글