이 문제를 요약하면 원하는 조건을 만족하는 최소 길이의 연속된 구간을 구하는 것이다.
따라서 2개의 index를 잘 조절하여 해당 구간을 구해야 한다.
(이를 투 포인터 알고리즘이라고 한다.)
구간의 왼쪽 끝을 left, 오른쪽 끝을 right라고 하자.
이때, 구간의 상태는 다음 2가지를 만족한다.
1. 보석의 모든 종류가 1개 이상은 구간에 포함된다.
2. left 위치의 보석은 구간 안에 단 1개다.
구간 (left, right)는 해당 right 위치가 오른쪽 끝이 되는 구간들 중 문제의 조건을 만족하는 최소 길이 구간이 된다.
그러나 아직 이것 만으로는 이 구간이 답인지 속단할 수 없다.
구간의 끝점을 옮겨 가면서 모든 경우를 따져야 한다.
( left를 한 칸씩 이동시키는 이유는 (left, right+1)에는 (left, right)가 포함되어 있기 때문이다. 더 간단히 말하면 어차피 (left, right+1)은 최소 길이가 될 수 없기 때문에 left+1로 증가 시켜 주는 것이다. )
#include <string>
#include <vector>
#include <set>
#include <unordered_map>
using namespace std;
bool find_gem(unordered_map<string, int> &umap, string &str) {
if (umap.find(str) == umap.end())
return false;
return true;
}
vector<int> solution(vector<string> gems) {
set<string> gem_kinds;
unordered_map<string, int> gem_cnt_map;
for (string &gem : gems)
gem_kinds.insert(gem);
int len = gems.size();
int kinds_cnt = gem_kinds.size();
int left = 0, answer_left, right = 0, answer_right;
int minLen = 100001;
string gem;
while(true) {
// left(현재 고정)부터 right까지 모든 종류의 보석 포함하는 최소의 right 구하기
while (right < len) {
gem = gems[right];
if (find_gem(gem_cnt_map, gem))
gem_cnt_map[gem]++;
else
gem_cnt_map[gem] = 1;
if (kinds_cnt == gem_cnt_map.size())
break;
right++;
}
// 위 while문을 빠져 나올 때, break가 아닌 while의 조건이 거짓이어서 빠져나왔을 경우 고려
if (kinds_cnt != gem_cnt_map.size())
break;
// left부터 right(현재 고정)까지 모든 종류의 보석 포함하는 최대의 left 구하기
while (left <= right) {
gem = gems[left];
if (gem_cnt_map[gem] == 1)
break;
gem_cnt_map[gem]--;
left++;
}
if (minLen > right-left+1) {
minLen = right-left+1;
answer_left = left+1;
answer_right = right+1;
}
gem_cnt_map.erase(gems[left]);
right++;
left++;
}
return {answer_left, answer_right};
}