Programmers. 보석 쇼핑
1. 문제설명
- 문자열로 된 보석 리스트가 주어진다.
- 모든 종류의 보석을 1개 이상 포함하는 가장 짧은 구간(인덱스 번호) 계산
2. 입/출력
- 입력
- 진열대 번호 순서대로 보석들의 이름이 저장된 배열
gems
- 진열대 번호 순서대로 보석들의 이름이 저장된 배열
- 출력
- 모든 보석을 하나 이상 포함하는 가장 짧은 구간의 시작 진열대 번호와 끝 진열대 번호를 차례대로 배열에 담아서 return
3. 문제 분류
- Two pointer
- 조건을 만족하는 구간을 탐색하는 알고리즘
- 알고리즘 수행에는 구간의 시작점, 끝점을 위한 변수가 필요하다.
- 일반적인 종료 조건은 끝점이 탐색 지점의 끝보다 작을 때이다.
- 구간을 탐색하는 거니까 어쩌면 당연한 말
4. 알고리즘 설계
- 알고리즘 수행에 앞서, 유일한 보석의 개수가 필요하다.
set<string> unique(gems.begin(), gems.end())
- set은 중복을 허용하지 않는 자료구조이다.
- 만약 중복된 원소가 insert되면 무시된다고 생각하면 이해가 빠르다.
- 투 포인터 수행을 위한
start
,end
초기화start
는 원소의 가장 처음인 0으로 지정- unordered_map을 통해 보석의 개수를 카운팅
- 유일한 보석의 개수와 unordered_map의 size가 같아지는 인덱스를 찾는다.
- 이 인덱스가
end
가 된다.
- 이 인덱스가
-
여기까지 수행했다면 가장 앞 부분의 모든 보석을 포함하는 가장 짧은 구간을 찾은 것이다.
- 이 구간은 언제나 가장 짧은 구간이 될 수 있을까?
- 정답은 아니다.
- 보석을 편의상 번호로 명명하고 다음 리스트를 살펴보자
- {1, 2, 2, 1, 1, 3, 4, 1}
- 0번째인 1부터 유일한 4에 해당하는 인덱스(6)로 초기화 될 것이다.
- 이 때의 리스트는 {1, 2, 2, 1, 1, 3, 4}가 된다.
- 1은 3개, 2는 2개 3과 4는 1개씩이다.
- 중복되는 원소가 있으니, 개수를 줄인다면 구간이 줄어들 것이다.
start
의 위치인 0번째부터 지워보자- 지워도 여전히 1은 2개 있어서 여유롭다.
- 1번째 위치의 2를 지워도 2가 한개가 남아있다.
- 2번째 위치의 2를 지우면 2가 0개가 된다.
- 주어진 리스트에서는 가장 짧은 구간을 구하는 데 성공했다.
- 여기까지 계산했을 때, 더 짧은 리스트는 절대로 존재할 수 없는가?
- 뒤에 있는 원소를 붙였는데 1개 이상의 보석을 가지는 더 짧은 리스트가 될 수 있다.
- 우리는 위에서 2를 지웠을 때 0개가 된다는 점을 이용해 구간을 구했다.
- 그렇다면 뒷쪽에서 가장 먼저 만나는 2를 찾고,
- 이전 구간과 새로운 구간의 길이를 비교하면 된다.
- 2를 찾지 못한다면, 인덱스가 배열의 크기보다 커지기 때문에 바로 종료하면 된다.
- 2를 찾지 못하면 모든 보석을 포함한다는 조건을 무너뜨리기 때문이다.