Coding Test

[Coding Test][Python][프로그래머스] Level 3 보석 쇼핑

어떻게든 되겠지~ 2025. 4. 30. 14:49

Lv.3 보석 쇼핑

 

문제)

개발자 출신으로 세계 최고의 갑부가 된 어피치는 스트레스를 받을 때면 이를 풀기 위해 오프라인 매장에 쇼핑을 하러 가곤 합니다.
어피치는 쇼핑을 할 때면 매장 진열대의 특정 범위의 물건들을 모두 싹쓸이 구매하는 습관이 있습니다.
어느 날 스트레스를 풀기 위해 보석 매장에 쇼핑을 하러 간 어피치는 이전처럼 진열대의 특정 범위의 보석을 모두 구매하되 특별히 아래 목적을 달성하고 싶었습니다.

진열된 모든 종류의 보석을 적어도 1개 이상 포함하는 가장 짧은 구간을 찾아서 구매

 

예를 들어 아래 진열대는 4종류의 보석(RUBY, DIA, EMERALD, SAPPHIRE) 8개가 진열된 예시입니다.

진열대 번호 1 2 3 4 5 6 7 8
보석 이름 DIA RUBY RUBY DIA DIA EMERALD SAPPHIRE DIA

진열대의 3번부터 7번까지 5개의 보석을 구매하면 모든 종류의 보석을 적어도 하나 이상씩 포함하게 됩니다.

진열대의 3, 4, 6, 7번의 보석만 구매하는 것은 중간에 특정 구간(5번)이 빠지게 되므로 어피치의 쇼핑 습관에 맞지 않습니다.

진열대 번호 순서대로 보석들의 이름이 저장된 배열 gems가 매개변수로 주어집니다. 이때 모든 보석을 하나 이상 포함하는 가장 짧은 구간을 찾아서 return 하도록 solution 함수를 완성해주세요.
가장 짧은 구간의 시작 진열대 번호와 끝 진열대 번호를 차례대로 배열에 담아서 return 하도록 하며, 만약 가장 짧은 구간이 여러 개라면 시작 진열대 번호가 가장 작은 구간을 return 합니다.

제한 사항

  • gems 배열의 크기는 1 이상 100,000 이하입니다.
    • gems 배열의 각 원소는 진열대에 나열된 보석을 나타냅니다.
    • gems 배열에는 1번 진열대부터 진열대 번호 순서대로 보석이름이 차례대로 저장되어 있습니다.
    • gems 배열의 각 원소는 길이가 1 이상 10 이하인 알파벳 대문자로만 구성된 문자열입니다.

풀이)

※ 풀이 아이디어

1. Sliding Window 방식으로 K를 증가시켜 가면서 제일 먼저 찾은 윈도우의 시작, 끝점을 찾는다
==> 하지만, K가 계속 증가하면서 시간초과가 발생한다.
==> 다른 풀이 방법을 생각해야한다.
Two Pointer를 사용하여 시간복잡도를 줄인다.
1. 보석의 수를 저장하는 Dictionary로 관리
2. left, right를 모두 0번째 Index에서 시작한다(right를 n-1에서 시작하는 것이 아니다)
3. 보석이 모두 포함될 때 까지 right를 1칸씩 증가시킨다
4. 이후 left를 증가시키며 더 짧은 구간이 있는지 확인한다.
     문제의 조건에 따르면 동일한 길이가 존재하는 경우 left가 더 작은 구간을 선택해야 하기 떄문에 이를 주의해야한다

 

Two Pointer 풀이

def solution(gems):
    answer = [0, len(gems)-1]
    unique_gems = set(gems)
    left, right = 0, 0
    
    # right를 오른쪽으로 이동시키면서 보석이 모두 포함되면 왼쪽 이동
    gems_dict = {gems[left]:1}
    while left < len(gems) and right < len(gems):
        if len(gems_dict) == len(unique_gems):
            if right - left < answer[1] - answer[0]:
                answer = [left, right]
            else:
                gems_dict[gems[left]] -= 1
                if gems_dict[gems[left]] == 0:
                    gems_dict.pop(gems[left])
                left += 1
        else:
            right += 1
            if right == len(gems):
                break
            if gems[right] in gems_dict:
                gems_dict[gems[right]] += 1
            else:
                gems_dict[gems[right]] = 1
            
    return [answer[0]+1, answer[1]+1]

Sliding Window 풀이(시간 초과)

def solution(gems):
    # 시간 초과
    unique_gems = set(gems)
    k = len(unique_gems)

    while True:
        for i in range(len(gems)-k+1):
            if set(gems[i:i+k]) == unique_gems:
                return [i + 1, i+k]
        else:
            k += 1