[Algorithm] Programmers : 순위 검색 by Python

엄희관·2021년 2월 3일
1

Algorithm

목록 보기
89/128
post-thumbnail
post-custom-banner

[문제 바로가기] https://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 함수를 완성해 주세요.

(제한사항, 입출력 및 입출력예제는 상당히 길어서 생략하였습니다...)


💡 문제 풀이

효율성을 해결하기 위해서 많은 시간을 투자했던 문제...(Level 2가 맞는지 의문..😥)
덕분에 새로운 접근과 라이브러리를 경험할 수 있었다.
TMI) 정답률 : 정확성 44.07%, 효율성 4.49%

1차원적으로 문제를 해결하는 방법은 단순 반복문을 사용하는 것이다.
query에 있는 스트링을 주어진 info와 비교하여 만족하는 경우 카운팅해주면 된다.

코드는 다음과 같다.

'효율성'을 통과하지 못하여 오답!

def solution(info, query):
    answer = []
    for q in query:
        orders = q.split()
        cnt = 0
        for i in info:
            people = i.split()
            if orders[0] != "-":
                if orders[0] != people[0]:
                    continue
            if orders[2] != "-":
                if orders[2] != people[1]:
                    continue
            if orders[4] != "-":
                if orders[4] != people[2]:
                    continue
            if orders[6] != "-":
                if orders[6] != people[3]:
                    continue
            if int(people[4]) >= int(orders[7]):
                cnt += 1
        answer.append(cnt)
    return answer

효율성을 해결하기 위해서는 info에 있는 정보들을 하나씩 빼내어 검색조건으로 나올 수 있는 모든 경우에 대해서 미리 만들어 주는 것이다.

ex) "java backend junior pizza 150"와 같은 정보를 가진 지원자가 있을 경우

해당 지원자를 검색할 수 있는 경우는 아래와 같다.

  • " - - - - 100"
  • "java - - - 100"
  • "- backend - - 100",
  • "- - junior - 100"
  • ...(생략)
  • "java backend junior pizza 100"

즉, 점수를 제외한 4가지의 정보를 대상으로 검색하기 때문에 총 나올 수 있는 경우의수는 2x2x2x2 = 16가지다.

이를 테이블로 표현하면 다음과 같다.

출처 : https://tech.kakao.com/2021/01/25/2021-kakao-recruitment-round-1/

따라서, 모든 정보에 대해서 16가지의 경우를 key(정보 문자열)-value(점수) 형식으로 미리 만들어 준 후 query에 해당하는 점수들을 대상으로 값을 비교하여 카운팅해주면 된다.

step 1)
주어진 정보(info)들을 하나씩 탐색하여 앞서 언급한 테이블 처럼 자료를 만든다. → make_all_cases 함수

make_all_cases 함수에 공백(' ')을 기준으로 분리한 자료를 주면 조합을 이용하여 16가지의 경우를 만들어낸다.
→ 조합 + 인덱스를 이용하여 만들었다.

ex) 분리한 문자열 : "java", "backend", "junior", "pizza"이고
조합으로 0, 2의 인덱스를 뽑았으면 "-backend-pizza"의 결과값을 갖게된다.

step 2)
make_all_cases 함수로 만들어낸 16가지의 경우에 대해서 key-value 형식으로 all_people 딕셔너리에 자료를 담는다.

  • all_people : 모든 정보의 가능한 검색 형태와 점수를 담은 딕셔너리

이 때, 16가지의 경우는 info내의 다른 정보들과 동일한 형태의 결과값이 나올 수 있기 때문에, 해당 경우의 점수를 다 담고자 value에 리스트 자료구조를 이용하였다.

step 3)
모든 지원자들을 위와 같은 방식으로 분류한 후 해당 그룹에서 점수를 기준으로 오름차순 정렬한다. → bisect_left 를 사용하여 시간을 단축하기 위함

step 4)
마지막으로 query의 정보들을 하나씩 탐색하면서 모든 조건을 만족하고 기준 점수보다 높은 사람의 수를 파악하면 된다.

이 때, 주어진 query가 all_people에 존재하지 않은 정보일 수 있으므로 조건문을 통해 반드시 확인해준다. → 미확인시 런타임에러 발생

query의 기준점수보다 높은 사람을 찾을 때 반복문을 통해 개수를 파악하면 효율성 부분을 통과하지 못한다.
따라서, step 3)에 모든 그룹을 정렬했기 때문에 bisect_left(binary search 이용)를 이용하여 원하는 개수를 빠르게 파악할 수 있다.

최종 코드는 다음과 같다.

'효율성'까지 통과한 코드

from bisect import bisect_left
from itertools import combinations

def make_all_cases(temp):
    cases = []
    for k in range(5):
        for li in combinations([0, 1, 2, 3], k):
            case = ''
            for idx in range(4):
                if idx not in li:
                    case += temp[idx]
                else:
                    case += '-'
            cases.append(case)
    return cases

def solution(info, query):
    answer = []
    all_people = {}
    for i in info:
        seperate_info = i.split()
        cases = make_all_cases(i.split())
        for case in cases:
            if case not in all_people.keys(): all_people[case] = [int(seperate_info[4])]
            else: all_people[case].append(int(seperate_info[4]))

    for key in all_people.keys():
        all_people[key].sort()

    for q in query:
        seperate_q = q.split()
        target = seperate_q[0] + seperate_q[2] + seperate_q[4] + seperate_q[6]
        if target in all_people.keys():
            answer.append(len(all_people[target]) - bisect_left(all_people[target], int(seperate_q[7]), lo=0, hi=len(all_people[target])))
        else:
            answer.append(0)

    return answer

통과 후 획득한 점수를 통해 쉽지 않은 문제였음을... 느꼈다 😂

profile
허브
post-custom-banner

0개의 댓글