框架
// 定义左右指针
int left = 0, right = 0;
// 维护一个 window 窗口
while (right < s.size()) {
window.add(s[right++]); // 右指针 向右移动
while (valid) {
window.remove(s[left++]); // 左指针向左移动
}
}
例题
最小覆盖字串(Leetcode 76)
1. 定义 left right 指针 以闭区间索引 window[left, right] 作为一个窗口
2. 不断增加 right 以扩大窗口 [left, right] 直到窗口中字符串符合要求
3. 停止增加 right 开始增加 left 以缩小窗口 [left, rihgt] 直到恰好不符合要求,同时,每次增加 left 我们更新一轮结果
4. 重复 2, 3 直到 right 到达字符串结尾
// c++
class Solution {
public:
string minWindow(string s, string t) {
int start = 0, minLen = INT_MAX;
int left = 0, right = 0;
unordered_map<char, int> window;
unordered_map<char, int> needs;
for (char c : t) needs[c]++;
int match = 0;
while (right < s.size()) {
char c1 = s[right];
if (needs.count(c1)) {
window[c1]++;
if (window[c1] == needs[c1]) match++;
}
right++;
while (match == needs.size()) {
if (right - left < minLen) {
// 更新最小字串的位置和长度
start = left;
minLen = right - left;
}
char c2 = s[left];
if(needs.count(c2)) {
window[c2]--;
if (window[c2] < needs[c2]) {
match--;
}
}
left++;
}
}
return minLen == INT_MAX ? "" : s.substr(start, minLen);
}
};
// Java
class Solution {
public String minWindow(String s, String t) {
if (s.length() == 0 || t.length() == 0) {
return "";
}
// Dictionary which keeps a count of all the unique characters in t.
Map<Character, Integer> dictT = new HashMap<Character, Integer>();
for (int i = 0; i < t.length(); i++) {
int count = dictT.getOrDefault(t.charAt(i), 0);
dictT.put(t.charAt(i), count + 1);
}
// Number of unique characters in t, which need to be present in the desired window.
int required = dictT.size();
// Left and Right pointer
int l = 0, r = 0;
// formed is used to keep track of how many unique characters in t
// are present in the current window in its desired frequency.
// e.g. if t is "AABC" then the window must have two A's, one B and one C.
// Thus formed would be = 3 when all these conditions are met.
int formed = 0;
// Dictionary which keeps a count of all the unique characters in the current window.
Map<Character, Integer> windowCounts = new HashMap<Character, Integer>();
// ans list of the form (window length, left, right)
int[] ans = {-1, 0, 0};
while (r < s.length()) {
// Add one character from the right to the window
char c = s.charAt(r);
int count = windowCounts.getOrDefault(c, 0);
windowCounts.put(c, count + 1);
// If the frequency of the current character added equals to the
// desired count in t then increment the formed count by 1.
if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
formed++;
}
// Try and contract the window till the point where it ceases to be 'desirable'.
while (l <= r && formed == required) {
c = s.charAt(l);
// Save the smallest window until now.
if (ans[0] == -1 || r - l + 1 < ans[0]) {
ans[0] = r - l + 1;
ans[1] = l;
ans[2] = r;
}
// The character at the position pointed by the
// `Left` pointer is no longer a part of the window.
windowCounts.put(c, windowCounts.get(c) - 1);
if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
formed--;
}
// Move the left pointer ahead, this would help to look for a new window.
l++;
}
// Keep expanding the window once we are done contracting.
r++;
}
return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
}
}
找到字符串中所有字母异位词(Leetcode 438)
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
unordered_map<char, int> need, window;
for (char c : p) need[c]++;
int left = 0, right = 0;
int valid = 0;
vector<int> res; // 记录结果
while (right < s.size()) {
char c = s[right];
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (right - left >= p.size()) {
// 当窗口符合条件时,把起始索引加入 res
if (valid == need.size())
res.push_back(left);
char d = s[left];
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
return res;
}
};
无重复字符串最长子串(Leetcode 3)
class Solution {
int window[128];
public:
int lengthOfLongestSubstring(string s) {
int left = 0, right =0;
int res = 0; // 记录最长的长度
while (right < s.size()) {
char c1 = s[right];
window[c1]++;
right++;
// 如果 window 中出现重读字符
// 开始移动 left 缩小窗口
while (window[c1] > 1) {
char c2 = s[left];
window[c2]--;
left++;
}
res = max(res, right - left);
}
return res;
}
};