문제 보기 · Level 2 · 2021 KAKAO BLIND RECRUITMENT · 정답률 36% · 푼 사람 11,285명 (2026-09-30 기준)1
문제
지원자마다 "언어 직군 경력 소울푸드 점수"가 주어진다. 문의는 "조건 and 조건 and 조건 and 조건 X" 꼴이고, 조건 자리의 -는 무엇이든 괜찮다는 뜻이다. 문의마다 조건을 만족하면서 점수가 X 이상인 지원자 수를 돌려준다. 정확성과 효율성을 따로 채점한다.
- 지원자 1~50,000명
- 문의 1~100,000개
- 점수 1~100,000
풀이
막히면 한 단계씩 펼친다.
필요한 개념
1단계 · 작은 예로 손으로 풀기
지원자 "java backend junior pizza 150" 한 명이 맞는 문의 조건은 네 칸마다 “그 값” 또는 -라서 16가지다.
- (java, backend, junior, pizza), (java, backend, junior, -), …, (-, -, -, -)
문의 "java and - and - and pizza 100"의 조건 (java, -, -, pizza)는 이 16가지 중 하나다. 그래서 지원자를 미리 16가지 조건 모양마다 넣어 두면, 문의가 오면 그 조건 모양 하나만 꺼내 보면 된다.
2단계 · 가장 단순한 방법과 그 속도
문의마다 지원자 5만 명을 모두 훑어 조건과 점수를 본다. 10만 × 5만 = 50억 번이다. 정확성 테스트는 통과해도 효율성 테스트에서 시간 초과가 난다.
3단계 · 핵심 아이디어
계산을 문의가 오기 전으로 옮긴다.
- 미리 모으기: 지원자마다 16가지 조건 모양을 만들어, 딕셔너리
groups[조건 모양]의 목록에 점수를 넣는다. 5만 × 16 = 80만 번이다. - 정렬: 목록마다 점수를 정렬한다.
- 문의: 조건 모양으로 목록을 꺼내고,
bisect_left(목록, X)로 X 이상이 시작되는 자리를 찾는다. 목록 길이에서 그 자리를 빼면 X 이상인 사람 수다.
떠올리는 신호: “같은 모양의 질문이 아주 많이 온다” → 질문이 오기 전에 답하기 쉬운 모양으로 정리해 둔다.
4단계 · 코드 흐름
- 빈
defaultdict(list)를 만든다. - 지원자마다 네 항목과 점수를 나누고, 항목마다 (값, “-“) 두 가지를 조합해 16개 키를 만들어 점수를 넣는다.
- 모든 목록을 정렬한다.
- 문의마다 “ and “를 빼고 나눠 네 조건과 X를 얻는다. 목록을 꺼내
len - bisect_left를 답에 넣는다.
5단계 · 파이썬 코드
from bisect import bisect_left
from collections import defaultdict
from itertools import product
def solution(info, query):
groups = defaultdict(list) # 조건 모양 → 점수 목록
for line in info:
*fields, score = line.split() # 앞 넷은 fields, 마지막은 score
for key in product(*[(f, "-") for f in fields]): # 16가지 조건 모양
groups[key].append(int(score))
for scores in groups.values():
scores.sort()
answer = []
for q in query:
parts = q.replace(" and ", " ").split() # ['java', '-', '-', 'pizza', '100']
key, x = tuple(parts[:4]), int(parts[4])
scores = groups.get(key, [])
answer.append(len(scores) - bisect_left(scores, x))
return answer
*fields, score = …: 나눈 조각 중 마지막 하나를 score에, 나머지를 리스트 fields에 담는다.product(*[(f, "-") for f in fields]): 네 칸마다 (값, “-“) 중 하나씩 고르는 모든 조합이다.*는 리스트를 풀어 따로따로 넘긴다.bisect_left(scores, x): x보다 작은 점수의 개수다. 전체에서 빼면 x 이상인 수다.groups.get(key, []): 그 조건 모양의 지원자가 없으면 빈 목록이다.
6단계 · 실행 추적
문의 "- and backend and senior and - 150" → 키 (-, backend, senior, -)
- 이 키에 들어간 지원자:
cpp backend senior pizza 260,python backend senior chicken 50 - 정렬한 점수 [50, 260],
bisect_left([50, 260], 150)= 1 - 2 − 1 = 1명
예시의 여섯 문의에 대한 답은 [1, 1, 1, 1, 2, 4]다.
7단계 · 복잡도와 흔한 실수
미리 모으기 \(O(16N)\), 정렬 \(O(16N \log N)\), 문의마다 \(O(\log N)\)이다. N = 5만, 문의 10만이면 합쳐서 수백만 번이다.
- 문의마다 지원자를 훑어 효율성에서 떨어진다.
bisect_right를 써서 X와 같은 점수를 빼먹는다. “X 이상”이니bisect_left다.- 문의의 “ and “를
split("and")로 잘라 빈칸이 붙은 조각이 생긴다. - 목록을 문의마다 다시 정렬한다. 정렬은 미리 한 번만 한다.
-
프로그래머스 스쿨, “순위 검색” (https://school.programmers.co.kr/learn/courses/30/lessons/72412), 2026-09-30 조회. ↩