문제의 조건에서 이므로, 으로 불가능하다.
처음에 떠올린 알고리즘은 다음과 같다.
보석의 구간을 구하는 것이니 투 포인터를 이용하면 되겠다고 생각했다.
# 1-index
from collections import defaultdict
def solution(gems):
def check_gems():
return all(list(gem_dict.values()))
answer = []
# 딕셔너리 = (종류 : 개수)
gem_dict = defaultdict(int)
for gem in gems:
gem_dict[gem] += 1
n = len(gems)
l, r = 0, n - 1
can_change_l, can_change_r = True, True # 증감이 가능한지
while l < r:
while r > l and can_change_r:
gem_dict[gems[r]] -= 1
if gem_dict[gems[r]] == 0:
gem_dict[gems[r]] += 1
can_change_r = False
else:
r -= 1
answer.append([l + 1, r + 1])
while l < r and can_change_l:
gem_dict[gems[l]] -= 1
if gem_dict[gems[l]] == 0:
gem_dict[gems[l]] += 1
can_change_l = False
else:
l += 1
answer.append([l + 1, r + 1])
break
answer.sort(key=lambda x: ((x[1] - x[0]), x[0]))
return answer[0]
위 코드의 알고리즘은 다음과 같다.
gems
에 속해있는 모든 보석의 종류와 개수를 딕셔너리에 저장한다.l = 0, r = n - 1
에서 시작해서 투 포인터를 진행한다.r
부터 크기를 1씩 줄여보면서, 만약 줄였을 때 모든 보석이 속해있지 않다면 r을 1 증가시키고 더 이상 r을 변경할 수 없다는 Flag
인 can_change_r
을 False
로 바꾼다.answer
에 현재 범위를 추가한다.([l + 1, r + 1]
)l
을 1씩 증가시키면서, 만약 증가시켰을 때 모든 보석이 속해있지 않다면 l을 1 감소시키고 더 이상 l을 변경할 수 없다는 Flag
인 can_change_l
을 False
로 바꾼다.answer
에 현재 범위를 추가한다.([l + 1, r + 1]
)어떤 오류가 존재할까?
위 코드는 r
을 먼저 줄이고 l
을 키우면서 범위를 확인한다.
이렇게 되면 초기 r
일 때 범위를 확인할 수 없다.
예를 들어 [l + 3, r - 2]
가 모든 보석을 포함하는 가장 짧은 구간이라고 하자.
r
을 먼저 줄일 때 r - 4
까지 줄일 수 있다고 하자.(즉, r - 5
까지 줄이면 모든 보석을 포함하지 않는다.)
그 다음으로 l
을 증가시키면, l의 크기가 어쨌든 r - 2
까지의 범위는 구할 수 없다.
r이 이미 r - 4이기 때문이다.
✅ 투 포인터는
l
과r
이 모두0
에서부터 시작한다.
내 코드는r = n - 1
에서 시작하므로, 투 포인터 알고리즘이라고 부를 수 없어보인다.투 포인터 개념에 대해서 다시 한번 공부해야겠다.
테스트 1 〉 통과 (0.03ms, 10.3MB) 테스트 2 〉 실패 (0.03ms, 10.2MB) 테스트 3 〉 실패 (0.16ms, 10.1MB) 테스트 4 〉 통과 (0.20ms, 10.2MB) 테스트 5 〉 통과 (0.14ms, 10.3MB) 테스트 6 〉 통과 (0.01ms, 10.1MB) 테스트 7 〉 실패 (0.01ms, 10.2MB) 테스트 8 〉 실패 (0.27ms, 10.1MB) 테스트 9 〉 실패 (0.42ms, 10.4MB) 테스트 10 〉 통과 (0.35ms, 10.2MB) 테스트 11 〉 통과 (0.44ms, 10.3MB) 테스트 12 〉 실패 (0.69ms, 10.2MB) 테스트 13 〉 실패 (1.23ms, 10.3MB) 테스트 14 〉 통과 (0.80ms, 10.3MB) 테스트 15 〉 실패 (1.19ms, 10.4MB) 효율성 테스트 테스트 1 〉 실패 (5.79ms, 10.5MB) 테스트 2 〉 실패 (1.91ms, 10.5MB) 테스트 3 〉 실패 (4.93ms, 11MB) 테스트 4 〉 통과 (3.38ms, 11.7MB) 테스트 5 〉 실패 (7.25ms, 11.8MB) 테스트 6 〉 실패 (16.18ms, 12.2MB) 테스트 7 〉 실패 (10.26ms, 12.6MB) 테스트 8 〉 실패 (12.70ms, 13MB) 테스트 9 〉 실패 (15.75ms, 13.5MB) 테스트 10 〉 실패 (15.38ms, 13.9MB) 테스트 11 〉 실패 (19.43ms, 14.7MB) 테스트 12 〉 통과 (11.88ms, 15.5MB) 테스트 13 〉 실패 (16.72ms, 16.2MB) 테스트 14 〉 실패 (29.89ms, 17MB) 테스트 15 〉 통과 (32.08ms, 17.9MB)
정확성: 15.6
효율성: 13.3
합계: 28.9 / 100.0
# 1-index
from collections import defaultdict
def solution(gems):
answer = []
n = len(gems)
set_size = len(set(gems))
gem_dict = {gems[0]: 1}
l, r = 0, 0
can_change_l, can_change_r = True, True # 증감이 가능한지
while l < n and r < n:
# 모든 보석 종류가 범위 안에 존재한다면
if len(gem_dict) == set_size:
answer.append([l + 1, r + 1])
gem_dict[gems[l]] -= 1
if gem_dict[gems[l]] == 0:
del gem_dict[gems[l]]
l += 1
else:
r += 1
if r == n:
break
gem_dict[gems[r]] = gem_dict.get(gems[r], 0) + 1
answer.sort(key=lambda x: ((x[1] - x[0]), x[0]))
return answer[0]
l
, r
을 모두 0부터 시작한다.
보석의 종류는 딕셔너리에 저장하고, 모든 보석을 포함할 때 까지 r
포인터를 증가시키고, 해당 위치의 보석을 딕셔너리에 추가 or 갱신
한다.(단, 범위에 벗어나지 않도록)
모든 보석의 종류를 포함한다면, 구간을 저장하고 l
을 증가시켜서 가장 짧은 구간을 찾는다.