(1-3) 코딩테스트 문제 풀이

Yongjoo Lee·2020년 12월 2일
0
post-thumbnail
post-custom-banner

파이썬을 이용하여 코딩테스트 문제를 풀어본다

Step 1 해시(Hash)

번호로 값에 접근할 수 있는 배열 대신 문자열로 값에 접근할 수 있는 해시를 이용

→ Python에서 dict 자료형 이용하여 문제를 해결한다

문제: 완주하지 못한 선수

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
  • completion의 길이는 participant의 길이보다 1 작습니다.
  • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
  • 참가자 중에는 동명이인이 있을 수 있습니다.

입출력 예

participant completion return
[leo, kiki, eden] [eden, kiki] leo
[marina, josipa, nikola, vinko, filipa] [josipa, filipa, marina, nikola] vinko
[mislav, stanko, mislav, ana] [stanko, ana, mislav] mislav

입출력 예 설명

예제 #1leo는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.

예제 #2vinko는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.

예제 #3mislav는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.

풀이1: dict 이용

def solution(participant, completion):
    d = {}
    for x in participant:
        d[x] = d.get(x,0) + 1
    for x in completion:
        d[x] -= 1
    dnf = [k for k, v in d.items() if v > 0]
    return dnf[0]

풀이2: 정렬 이용

def solution(participant, completion):
    sorted_parti = sorted(participant)
    sorted_compl = sorted(completion)
    for p, c in zip(sorted_parti, sorted_compl + ['']):
        if p != c:
            return p

O(nlogn)O(nlogn) 이므로 해시를 이용한 풀이1보다 오래걸림

Step 2 탐욕법(Greedy)

탐욕법(Greedy Algorithm)

알고리즘의 각 단계에서 그 순간에 최적이라고 생각되는 것을 선택

(현재의 선택이 마지막 해답의 최적성을 해치지 않을 때 사용 가능)

문제: 체육복

문제 설명

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

입출력 예

n lost reserve return
5 [2, 4] [1, 3, 5] 5
5 [2, 4] [3] 4
3 [3] [1] 2

입출력 예 설명

예제 #11번 학생이 2번 학생에게 체육복을 빌려주고, 3번 학생이나 5번 학생이 4번 학생에게 체육복을 빌려주면 학생 5명이 체육수업을 들을 수 있습니다.

예제 #23번 학생이 2번 학생이나 4번 학생에게 체육복을 빌려주면 학생 4명이 체육수업을 들을 수 있습니다.

풀이1

  1. 학생 수 + 2(맨앞과 맨뒤에 하나씩 추가) 만큼 [1] 리스트를 생성한다.
  2. 빌려줄 수 있는 사람의 리스트를 돌면서 +1 더하고, 잃어버린 사람의 리스트를 돌면서 -1 더한다.
  3. 1부터 n 까지 돌면서
    1. 앞의 사람이 0이고 내가 2이면 둘 다 1로 바꿔준다
    2. 내가 2이고 뒤의 사람이 0이면 둘 다 1로 바꿔준다.
def solution(n, lost, reserve):
    u = [1] * (n + 2)
    for i in reserve:
        u[i] += 1
    for i in lost:
        u[i] -= 1
    for i in range(1, n + 1):
        if u[i - 1] == 0 and u[i] == 2:
            u[i - 1:i + 1] = [1, 1]
        elif u[i] == 2 and u[i + 1] == 0:
            u[i:i + 2] = [1, 1]
    return len([a for a in u[1:-1] if a > 0])

→ 복잡도: O(n)O(n)

풀이2

O(n)O(n) 보다 낮은 복잡도 알고리즘은 어렵겠지만

여벌의 체육복을 가져온 학생이 매우 적다면

  • 여벌의 체육복을 가져온 학생들의 번호(reserve)를 정렬하고, 이것을 하나 하나 순서대로 살펴보면서 빌려줄 수 있는 다른 학생을 찾아서 처리한다.
def solution(n, lost, reserve):
    s = set(lost) & set(reserve)
    l = set(lost) - s
    r = set(reserve) - s
    for a in sorted(r):             # 1) k
        if a - 1 in l:              # 2) m
            l.remove(a - 1)         # 3) m
        elif a + 1 in l:
            l.remove(a + 1)
    return n - len(l)

여벌의 체육복을 가져온 학생들의 번호(reserve)를 정렬

→ 복잡도: O(kLogk)O(kLogk)

코드를 보면 rr의 개수동안 ll안에 존재하는 지 찾고, 존재하면 ll에서 제거(remove 메서드) 함으로, rr의 개수를 kk, ll의 개수를 mm 이라고 할 때 복잡도는 O(kmm)O(k * m * m)으로 O(kLogk)O(kLogk) 보다 더 커지는 것이 아닌지...🤔

[ 20.12.02 추가💬 ]

Python에서 set요소 삭제(remove)요소 포함 여부 확인(a in b)에 대한 복잡도는 O(1)O(1) 이다!

참고자료: 파이썬 자료형 및 연산자의 시간 복잡도

Step 3 탐욕법 (2)

문제: 큰 수 만들기

문제 설명

어떤 숫자에서 k개의 수를 제거했을 때 얻을 수 있는 가장 큰 숫자를 구하려 합니다.

예를 들어, 숫자 1924에서 수 두 개를 제거하면 [19, 12, 14, 92, 94, 24] 를 만들 수 있습니다. 이 중 가장 큰 숫자는 94 입니다.

문자열 형식으로 숫자 number와 제거할 수의 개수 k가 solution 함수의 매개변수로 주어집니다. number에서 k 개의 수를 제거했을 때 만들 수 있는 수 중 가장 큰 숫자를 문자열 형태로 return 하도록 solution 함수를 완성하세요.

제한 조건

  • number는 1자리 이상, 1,000,000자리 이하인 숫자입니다.
  • k는 1 이상 number의 자릿수 미만인 자연수입니다.

입출력 예

number k return
1924 2 94
1231234 3 3234
4177252841 4 775841

풀이

  • 앞 자리에서부터 큰 수를 모은다.
  • 제거해야 할 개수가 남아 있으면 그 만큼을 뒤에서부터 자른다.
def solution(number, k):
    collected = []
    for i, num in enumerate(number):
        while len(collected) > 0 and collected[-1] < num and k > 0:
            collected.pop()
            k -= 1
        collected.append(num)
    collected = collected[:-k] if k > 0 else collected
    return ''.join(collected)

Step 4 정렬(Sort)

문제: 가장 큰 수

문제 설명

0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.

예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.

0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.

제한 사항

  • numbers의 길이는 1 이상 100,000 이하입니다.
  • numbers의 원소는 0 이상 1,000 이하입니다.
  • 정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.

입출력 예

numbers return
[6, 10, 2] 6210
[3, 30, 34, 5, 9] 9534330

풀이

최대 숫자 (1000) 의 글자 개수만큼 반복한 문자열을 key로 하여 내림차순으로 정렬한다.

def solution(numbers):
    str_nums = list(map(str, numbers))
    max_num_len = 4

    def repeat_max_len(s):
        q, r = divmod(max_num_len, len(s))
        return s * q + s[:r + 1]

    sorted_str_nums_by_max_len = sorted(str_nums, key=repeat_max_len, reverse=True)
    if sorted_str_nums_by_max_len[0] == '0':
        return '0'
    return ''.join(sorted_str_nums_by_max_len)
profile
하나씩 정리하는 개발공부로그입니다.
post-custom-banner

0개의 댓글