2020 카카오 인턴십 - 67258. 보석 쇼핑
Python 풀이
풀이 1
Two Pointer(투 포인터) 알고리즘을 이용해 풀이.
외국에선.. Two Pointer Technique(투 포인터 테크닉) 이라고 부르는 듯.
start, end Pointer를 정하고,
처음엔 조건을 만족할 때까지 end += 1
그 이후, 조건을 만족하는 한 start += 1
그렇게 조건을 만족하는 [start, end]를 구함.
그 이후, 뒤쪽에 더 짧은 답이 존재할 수 있으니
end에 +1을 해주며, end < len(gems) 이하에서 반복.
그렇게 Two Pointer를 이용해 풀이하였으나,
시간 초과.
Code
def solution(gems): gemList = len(set(gems)) start = 0 end = 0 answer = [0, len(gems)] def A(): a = len(set(gems[start:end+1])) if a == gemList: return True else: return False while True: while True: if A(): break end += 1 while True: start += 1 if A() == False: start -= 1 break if (answer[1]-answer[0]) > (end - start): answer = [start, end] if end == len(gems)-1: break else: end += 1 return [answer[0]+1, answer[1]+1]
풀이 2
위의 방법은,
def A(): a = len(set(gems[start:end+1])) if a == gemList: return True else: return False
를 각 순회마다 실행하는 데다, 위 함수 자체가 리스트를 슬라이싱 후 하나하나 set으로 변경하기에 시간이 오래 걸린다.
따라서, 구간이 모든 보석을 하나 이상 포함하는 지 검사할 때, 그 때마다 리스트를 슬라이싱해 검사하지 말고,
dictionary를 이용하면 key값의 개수만 세고도 검사할 수 있다.
진열대 번호 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
보석 이름 | DIA | RUBY | RUBY | DIA | DIA | EMERALD | SAPPHIRE | DIA |
에서 [3, 7]의 구간을 dictionary로 표현한다면, DIA가 2개, RUBY 1개, EMERALD 1개, SAPPHIRE 1개이므로
{DIA: 2, RUBY: 1, EMERALD: 1, SAPPHIRE: 1}
과 같이 만들 수 있다.
이렇게 되면, dictionary의 key 개수만 비교한다면 구간이 모든 보석을 하나 이상 포함하는 지 검사할 수 있기에, 시간이 단축된다.
Two Pointer를 이용해 구간을 탐색하면서, end += 1을 할 때는 key값이 존재한다면 value를 +1, key값이 없다면 key값을 추가하고,
start += 1을 하여 구간의 길이가 줄어들었을 때는, value == 0 이 된 key값이 있다면 그 key값을 삭제해 주며 반복한다.
위와 같은 예시에서는, [3, 7]의 구간에서 start += 1을 하여 [4, 7]의 구간으로 변경한다면,
{DIA: 2, RUBY: 0, EMERALD: 1, SAPPHIRE: 1}
이 되는데, RUBY는 0개(구간에 포함되지 않음) 이므로,
{DIA: 2, EMERALD: 1, SAPPHIRE: 1}
와 같이 수정해주는 식으로 진행하면 된다.
Code
def solution(gems): gemList = {gems[0]: 1} start = 0 end = 0 answer = [0, len(gems)] kindOfGem = len(set(gems)) def add(gem): if gem in gemList: gemList[gem] += 1 else: gemList[gem] = 1 def remove(gem): if gemList[gem] == 1: del gemList[gem] else: gemList[gem] -= 1 while start <= end and end < len(gems): if kindOfGem == len(gemList): if answer[1]-answer[0] > end-start: answer = [start, end] remove(gems[start]) start += 1 elif kindOfGem != len(gemList): end += 1 if end == len(gems): break add(gems[end]) return [answer[0]+1, answer[1]+1]
이 부분에서 조건을 달아 break하는 방식과, 다른 loop 방식으로 풀이하려 해서 어려움을 겪었는데,
아래 블로그의 코드를 참고하여 해결하였다.
while start <= end and end < len(gems): if kindOfGem == len(gemList): if answer[1]-answer[0] > end-start: answer = [start, end] remove(gems[start]) start += 1 elif kindOfGem != len(gemList): end += 1 if end == len(gems): break add(gems[end])