문제
https://school.programmers.co.kr/learn/courses/30/lessons/72412
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
굉장히 간단해보이지만.. 효율성 테스트때문에 시간이 오래 걸렸던 문제!
주요 아이디어
처음에는 그냥 단순하게 비교만 하면 된다고 생각했다..!
- info, query를 공백 기준으로 split하고, 맨 끝의 score는 따로 배열을 만들어 넣었다
- query의 '-' 는 해당 조건은 보지 않겠다는 뜻이므로 가능한 경우의 수를 다 집어넣었다
코드 구현 (Python 3)
더보기
def solution(info, query):
answer = []
# info 끊어서 저장
info_list = [x.split() for x in info]
info_score = [int(x[-1]) for x in info_list]
info_list = [x[:-1] for x in info_list]
# query도 끊어서
query_list = [x.split(' and ') for x in query]
# '-' 대체용
total_dict = {"0" : ["cpp", "java", "python"], "1" : ["backend", "frontend"],
"2" : ["junior", "senior"], "3" : ["chicken", "pizza"]}
for idx, q in enumerate(query_list):
query_list[idx] = q[:-1] + q[-1].split(' ')
query_score = [int(x[-1]) for x in query_list]
query_list = [x[:-1] for x in query_list]
for q in range(len(query_list)):
for i in range(len(query_list[q])):
if query_list[q][i] == '-':
query_list[q].extend(total_dict[str(i)])
# 비교
for idx, q in enumerate(query_list):
cnt = 0
for i, info in enumerate(info_list):
if set(info).issubset(set(q)) and info_score[i] >= query_score[idx]:
cnt += 1
answer.append(cnt)
return answer
테스트 케이스는 통과했으나 효율성 테스트에서 여지 없이 실패..ㅠㅠ
실패 원인
실패 원인과 정확한 솔루션을 알아보고자 해당 서적을 참고했다.
- info 배열의 크기는 최대 50,000이다
- query 배열의 크기는 최대 100,000 이다
- 따라서 하나씩 대조하는 방법으로 한다면, 최대 50,000 * 100,000 = 50억의 반복횟수가 된다!
이렇듯 효율성을 체크하는 문제에서는 O(n^2) 시간 복잡도가 나온다면 다시 푸는 것이 방법일 것 같다..
비교 탐색을 반드시 수행해야 하는데 양이 많은 경우 이분 탐색을 활용하자!
- 주어진 조건에서 가능한 모든 조합을 생각해보자
예시 1. java, backend, junior, pizza를 만족시키는 모든 문의 조건
더보기
java | backend | junior | pizza |
- | backend | junior | pizza |
java | - | junior | pizza |
java | backend | - | pizza |
java | backend | junior | - |
- | - | junior | pizza |
- | backend | - | pizza |
- | backend | junior | - |
java | - | - | pizza |
java | - | junior | - |
java | backend | - | - |
- | - | - | pizza |
- | - | junior | - |
- | backend | - | - |
java | - | - | - |
- | - | - | pizza |
- | - | - | - |
하나의 지원자 조건에 해당하는 문의 조건은 총 16가지
미리 선택 가능한 사항별로 경우의 수를 구하면 단순 조회만으로 결과를 알아낼 수 있다!
-> 모든 경우의 수를 딕셔너리 자료형으로 만들어 O(1) 시간 안에 확인 가능하도록 하자
그러니까 조건에 맞는 -> 지원자를 탐색하는 것이 아니라
해당 지원자가 가능한 경우의 수를 만들어놓고 -> 이에 맞는 조건을 찾는 것이다!
- defaultdict를 통해 "지원자 조건" : [점수] 형태로 딕셔너리를 구성한다
- 이 때 "-" 를 고려해서 모든 경우의 수를 형성한다
- 공백을 모두 제거하고 concat해 문자열 형태로 key를 만든다
- 점수를 기준으로 정렬한다
- query 문을 순회하며 조건에 해당하는 것들 중, 테스트 score에 미달하는 것들만 제거해 길이를 반환한다
- bisect_left 함수를 통해 점수 하한선을 구할 수 있다
코드 재구현 (Python 3)
제출 결과
from itertools import combinations
from collections import defaultdict
from bisect import bisect_left
def solution(info, query):
answer = []
# 지원자 info를 돌면서 가능한 경우의 수를 모두 만들어보자
applicants = defaultdict(list)
for i in info:
applicant = i.split()
score = int(applicant.pop()) # 점수는 따로 분리해주기
# 해당 조건에 해당하는 모든 지원자의 점수를 value로 append
applicants[''.join(applicant)].append(score)
# 모든 경우의 수 만들어내기
for j in range(4):
candi = list(combinations(applicant, j))
for c in candi:
applicants[''.join(c)].append(score)
# 정렬하기
for i in applicants:
applicants[i].sort()
for i in query:
key = i.split()
score = int(key.pop())
key = ''.join(key)
key = key.replace('and', '').replace(' ', '').replace('-', '')
answer.append(len(applicants[key]) - bisect_left(applicants[key], score))
return answer
코드 개선 방안
효율성 테스트가 있는 문제에서는 시간 복잡도를 꼭 체크하자!
'알고리즘 > 프로그래머스' 카테고리의 다른 글
[프로그래머스] 튜플 (0) | 2024.04.07 |
---|---|
[프로그래머스] 두 개 뽑아서 더하기 (0) | 2024.04.07 |
[프로그래머스] 소수 찾기 (0) | 2024.04.07 |
[프로그래머스] 사칙연산 (0) | 2024.04.05 |
[프로그래머스] 퍼즐조각 채우기 (0) | 2024.04.02 |