滑动窗口概念
维护一个窗口[left,right), 当前窗口内的元素没有达到要求的解时,继续扩大right, 直到窗口内的元素达到要求的解。注意此时只是一个解,而不是最优解,要想得到最优解需要left++来缩小窗口。因此求解过程分为两步:
- right++ 来增加窗口大小寻找一个解
- left++来缩小窗口大小寻找最优解
关键点:什么时候窗口停止增大并开始减小
滑动窗口实战1

- 当窗口内的字符包含了t中的所有字符时,窗口停止增大
- 记录当前窗口内的解的长度,长度更短则更新
- 开始缩小窗口,判断缩小窗口后的窗口内的字符是否还满足条件,如果满足条件继续计算窗口大小,并更新解
- 重复以上过程直到遍历完s
class Solution {public:string minWindow(string s, string t) {unordered_map<char,int> window,need;//need记录t中各类字符出现的次数for(char c:t)need[c]++;//初始化窗口左右边界int left=0,right=0;//valid记录的是窗口内满足条件的字符种类数int valid=0,len=INT_MAX;//start记录最优解的开始位置int start=0;while(right<s.size()){char c=s[right];right++;//右边界先增大 因此窗口是[left,right) 左闭右开if(need.count(c))//当前字符是t中的{window[c]++;//窗口内对应种类的字符+1if(window[c]==need[c])//某种字符个数满足valid++;}while(valid==need.size())//只要窗口内的字符是符合条件的一个解 就继续优化解{char d=s[left];//获取窗口左边界字符if(need.count(d))//左边界字符是t中的{if(need[d]==window[d])//如果该类字符已经满足条件 移掉它之后 符合条件的种类数就会-1valid--;window[d]--;//窗口内该类字符-1}if(right-left<len)//窗口更小则更新{len=right-left;start=left;//记录新的解的开始位置}left++;//此时left++ 因为前面需要right-left}}return len==INT_MAX?"":s.substr(start,len);}};//O(n) n是s的长度//O(C) c是t中的字符种类数
滑动窗口实战2

这道题比较于上一道题目,区别有以下几点:
- 窗口收缩条件是窗口内的字符个数=s1.size(), 因为是排列,所以不能有多余的字符
- 不存在最优解,找到一个解即返回true
class Solution {public:bool checkInclusion(string s1, string s2) {unordered_map<char,int> window,need;//need记录t中各类字符出现的次数for(char c:s1)need[c]++;//初始化窗口左右边界int left=0,right=0;int valid=0;while(right<s2.size()){char c=s2[right];right++;if(need.count(c)){window[c]++;if(window[c]==need[c])valid++;}if(right-left==s1.size())//缩小条件{if(valid==need.size())//找到一个解即返回truereturn true;char d=s2[left];if(need.count(d)){if(window[d]==need[d])valid--;window[d]--;}left++;}}return false;}};//O(s2.size())//O(c) c是s1中含有的字符种类数
滑动窗口实战3

这一题和上一题类似,需要改动的只有一处:
if(valid==need.size())//找到一个解即返回truereturn true;if(valid==need.size())//记录下所有的解ans.push_back(left);
class Solution {public:vector<int> findAnagrams(string s, string p) {vector<int> ans;unordered_map<char,int> window,need;//need记录t中各类字符出现的次数for(char c:p)need[c]++;//初始化窗口左右边界int left=0,right=0;int valid=0;while(right<s.size()){char c=s[right];right++;if(need.count(c)){window[c]++;if(window[c]==need[c])valid++;}if(right-left==p.size()){if(valid==need.size())ans.push_back(left);char d=s[left];if(need.count(d)){if(window[d]==need[d])valid--;window[d]--;}left++;}}return ans;}};//O(s.size())//O(c) c是p中含有的字符种类数
class Solution {public List<Integer> findAnagrams(String s, String p) {List<Integer> ans=new ArrayList<>();HashMap<Character,Integer> window=new HashMap<>();HashMap<Character,Integer> need=new HashMap<>();for(int i=0;i<p.length();i++){char c=p.charAt(i);need.put(c,need.getOrDefault(c,0)+1);}int valid=0;int left=0,right=0,n=s.length();while(right<n){char c=s.charAt(right);right++;if(need.containsKey(c)){window.put(c,window.getOrDefault(c,0)+1);if(window.get(c).equals(need.get(c)))valid++;}if(right-left>=p.length()){char c2=s.charAt(left);if(valid==need.size()){ans.add(left);}if(need.containsKey(c2)){if(window.get(c2).equals(need.get(c2)))valid--;window.put(c2,window.getOrDefault(c2,0)-1);}left++;}}return ans;}}
滑动窗口实战4

此题判断窗口收缩的条件时window[c]>1 并且可能需要多次移动 需要注意的时,对于不同的情况,有的窗口移动一次即可(比如实战2 3 此时可以用if语句),有的窗口需要移动多次(比如实战1 4 此时需要使用while语句)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> window;
int left=0,right=0;
int maxLen=0;
while(right<s.size())
{
char c=s[right];
right++;
window[c]++;
//需要用while 比如abcdd 需要left移动4次
while(window[c]>1)
{
char d=s[left];
window[d]--;
left++;
}
maxLen=max(right-left,maxLen);
}
return maxLen;
}
};
//O(s.size())
//O(c) c是s中含有的字符种类数
