PS/LeetCode
76. Minimum Window Substring - Streak 14
조금씩 차근차근
2025. 9. 27. 08:17
t를 전부 포함하는 최소 길이의 부분 문자열을 찾는 문제였다.
일단 최소 상태를 유지해야 하니 투 포인터가 떠올랐는데, "어떻게" 최소 상태를 유지할것인지에 대한 문제는 조금 까다로웠다.
나의 경우, 문자열이 포함되었을 때부터 왼쪽 끝을 당겨오면서 t를 포함하는지 체크하였다.
이때, 한 문자의 길이는 1이므로 왼쪽 문자의 이탈은 오른쪽에 바로 채워지지 않는 이상 더 짧아지기 어렵고, 따라서 항상 최소 상태 유지가 가능했다.
class Solution {
public String minWindow(String s, String t) {
int left = 0, right = 0;
int[] cnt = new int[58];
int[] need = new int[58];
for(int i = 0; i<t.length(); i++) need[t.charAt(i)-'A']++;
int min = Integer.MAX_VALUE;
String ret = "";
while(right < s.length()){
int now = s.charAt(right) - 'A';
cnt[now]++;
right++;
while(check(cnt, need) && left <= right){
if(min > right - left + 1){
ret = s.substring(left, right);
min = right - left + 1;
}
int before = s.charAt(left) - 'A';
cnt[before]--;
left++;
}
}
return ret;
}
private boolean check(int[] cnt, int[] need){
for(int i = 0; i<cnt.length; i++)
if(cnt[i] < need[i]) return false;
return true;
}
}
/**
최소 상태 유지가 관건
*/
현재는 58까지 순회해서 check를 검사하므로, 해시맵과 같은 자료구조를 사용한다면 상수배가 줄어 좀 더 빠를 것이다.
라고 마칠려고 했는데, O(n+m) 풀이를 요구받았다.
사실 현재 풀이는 이미 O(n+m)인데, 이 말은 마치 이것보다 더 느린 풀이가 있다는 말처럼 들렸다.
그래서 곰곰히 생각해봤고, O(mlogn)의 다소 재밌는 풀이를 생각해봤다.
- 최소 길이 부분 문자열은 반드시 그보다 긴 문자열에 포함된다.
- 문자열의 길이와 포함/미포함 여부는 최소 길이 문자열을 기준으로 T/F 구조가 된다.
- 따라서, 매개변수 탐색이 적용 가능하다.
class Solution {
public String minWindow(String s, String t) {
if (s.length() < t.length()) return "";
int[] need = new int[128];
for (char c : t.toCharArray()) need[c]++;
int lo = t.length(), hi = s.length();
int bestL = -1, bestLen = Integer.MAX_VALUE;
while (lo <= hi) {
int mid = (lo + hi) >>> 1;
int start = existsWindowOfLen(s, need, mid);
if (start != -1) {
if (mid < bestLen) { bestLen = mid; bestL = start; }
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return bestL == -1 ? "" : s.substring(bestL, bestL + bestLen);
}
private int existsWindowOfLen(String s, int[] need0, int L) {
if (L <= 0) return -1;
int[] need = new int[128];
int missingKinds = 0;
for (int c = 0; c < 128; c++) {
need[c] = need0[c];
if (need[c] > 0) missingKinds++;
}
int[] win = new int[128];
int satisfied = 0;
if (L <= s.length()) {
for (int i = 0; i < L; i++) {
int c = s.charAt(i);
if (need[c] > 0 && ++win[c] == need[c]) satisfied++;
}
if (satisfied == missingKinds) return 0;
} else return -1;
for (int r = L; r < s.length(); r++) {
int add = s.charAt(r);
int rem = s.charAt(r - L);
if (need[add] > 0 && ++win[add] == need[add]) satisfied++;
if (need[rem] > 0 && win[rem]-- == need[rem]) satisfied--;
if (satisfied == missingKinds) return r - L + 1;
}
return -1;
}
}