[Problem Solving] 순위 검색

Sean·2023년 10월 14일
0

Problem Solving

목록 보기
103/130

문제

https://school.programmers.co.kr/learn/courses/30/lessons/72412

카카오는 하반기 경력 개발자 공개채용을 진행 중에 있으며 현재 지원서 접수와 코딩테스트가 종료되었습니다. 이번 채용에서 지원자는 지원서 작성 시 아래와 같이 4가지 항목을 반드시 선택하도록 하였습니다.

  • 코딩테스트 참여 개발언어 항목에 cpp, java, python 중 하나를 선택해야 합니다.
  • 지원 직군 항목에 backend와 frontend 중 하나를 선택해야 합니다.
  • 지원 경력구분 항목에 junior와 senior 중 하나를 선택해야 합니다.
  • 선호하는 소울푸드로 chicken과 pizza 중 하나를 선택해야 합니다.

인재영입팀에 근무하고 있는 니니즈는 코딩테스트 결과를 분석하여 채용에 참여한 개발팀들에 제공하기 위해 지원자들의 지원 조건을 선택하면 해당 조건에 맞는 지원자가 몇 명인 지 쉽게 알 수 있는 도구를 만들고 있습니다.

예를 들어, 개발팀에서 궁금해하는 문의사항은 다음과 같은 형태가 될 수 있습니다.
코딩테스트에 java로 참여했으며, backend 직군을 선택했고, junior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 50점 이상 받은 지원자는 몇 명인가?

물론 이 외에도 각 개발팀의 상황에 따라 아래와 같이 다양한 형태의 문의가 있을 수 있습니다.

  • 코딩테스트에 python으로 참여했으며, frontend 직군을 선택했고, senior 경력이면서, 소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?
  • 코딩테스트에 cpp로 참여했으며, senior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?
  • backend 직군을 선택했고, senior 경력이면서 코딩테스트 점수를 200점 이상 받은 사람은 모두 몇 명인가?
  • 소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 250점 이상 받은 사람은 모두 몇 명인가?
  • 코딩테스트 점수를 150점 이상 받은 사람은 모두 몇 명인가?

즉, 개발팀에서 궁금해하는 내용은 다음과 같은 형태를 갖습니다.

* [조건]을 만족하는 사람 중 코딩테스트 점수를 X점 이상 받은 사람은 모두 몇 명인가?

지원자가 지원서에 입력한 4가지의 정보와 획득한 코딩테스트 점수를 하나의 문자열로 구성한 값의 배열 info, 개발팀이 궁금해하는 문의조건이 문자열 형태로 담긴 배열 query가 매개변수로 주어질 때,
각 문의조건에 해당하는 사람들의 숫자를 순서대로 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

시간초과 풀이

아이디어

  • 예를 들어 'java backend junior pizza 150'이 들어왔다고 치면, java, backend, junior, pizza에 대해서 모든 조합들(0개 선택, 1개 선택, 2개 선택, 3개 선택, 4개 선택)에 대해서 공백 없이 이어붙인 것을 key로 설정하고, 값을 [150]과 같이 주는 defaultdict를 사용한다.
    • 예를 들어, 다음과 같은 형식으로 저장된다.
    my_dictionary = {
      'java': [150],
      'javabackend': [150],
      'javapizza': [150],
      ...
    }
    • 그렇게 하기 위해서 itertoolscombinations와, collectionsdefaultdict를 사용해주었다.
    • 이렇게 저장함으로써 다양한 조합의 쿼리가 들어왔을 때 바로 dictionary의 키를 통해 조회할 수 있도록 설계하였다.
  • 또, 문제에서는 X점 이상의 사람들을 모두 구하라고 했으므로 bisect 라이브러리의 bisect_left를 통해서, 정렬된 scores 리스트에서 bisect_left(점수리스트, X점)을 통해서 X점이 가장 처음 나오는 위치를 알아낼 수 있고, 이를 scores 리스트의 길이에서 빼면 X점 이상의 사람들의 수가 나오는 아이디어를 이용하였다.

    Tip) [왼쪽값, 오른쪽값] 범위에 해당하는 수들의 '개수'를 구하라고 할 때는 bisect를 통해서 구하는 편이 훨씬 빠르다!! 이런 것이 나올 떄면 bisect를 꼭 떠올리도록 하자!!

코드

from itertools import combinations
from collections import defaultdict
from bisect import bisect_left

def solution(info, query):
    information = defaultdict(list)
    answer = []
    
    #정보 저장
    for p in info:
        atts = p.split(" ")
        for i in range(5):
            comb = list(combinations(atts[:4], i))
            for tup in comb:
                information["".join(tup)].append(int(atts[-1]))
    
    #쿼리 파싱 및 정보 습득
    for q in query:
        words = q.split(" and ")
        last_word = words[-1].split(" ")
        words[-1] = last_word[0]
        score = int(last_word[1])
        key = "".join([word for word in words[:4] if word != '-'])
        scores = information[key]
        scores.sort()
        answer.append(len(scores) - bisect_left(scores, score))
        
    return answer

정답 풀이

고친 부분

  • 위에 풀이가 거의 다 맞았지만, 사소한 부분 때문에 시간 초과가 났다.
  • 바로, 쿼리를 파싱하는 부분에서 매 쿼리마다 scores 리스트를 sort 해주었기 때문이다. 어차피 sort된 정보가 필요하다면 처음부터 모두 sort를 해주면 되는 것이었다.

고친 코드

from itertools import combinations
from collections import defaultdict
from bisect import bisect_left

def solution(info, query):
    information = defaultdict(list)
    answer = []
    
    #정보 저장
    for p in info:
        atts = p.split(" ")
        for i in range(5):
            comb = list(combinations(atts[:4], i))
            for tup in comb:
                information["".join(tup)].append(int(atts[-1]))
    
    #정보의 모든 점수들을 먼저 정렬해준다.
    for v in information.values():
        v.sort()
        
    #쿼리 파싱 및 정보 습득
    for q in query:
        words = q.split(" and ")
        last_word = words[-1].split(" ")
        words[-1] = last_word[0]
        score = int(last_word[1])
        key = "".join([word for word in words[:4] if word != '-'])
        scores = information[key]
        answer.append(len(scores) - bisect_left(scores, score))
        
    return answer
        

다시 보는 핵심 아이디어

  1. 다양한 조건의 쿼리를 만족하기 위해 itertoolscombinations를 활용.
  2. 범위에 해당하는 수들의 '개수'를 찾기 위해 순차탐색하면서 count+=1 해주는게 아니라, bisect 라이브러리의 bisect_left 또는 bisect_right의 인덱스 차이를 이용해서 구해낼 수 있다는 것.
profile
여러 프로젝트보다 하나라도 제대로, 깔끔하게.

0개의 댓글