主要参考:
【缓缓】使用「滑动窗口」即可简单求解 另附【算法框架】与【拓展题目】
我写了首诗,把滑动窗口算法变成了默写题
滑动窗口
其实就是快慢指针的一种
什么是滑动窗口:对一个序列(本题是一个字符串s)使用双指针left、right,初始化left = right = 0,把索引左闭右开的区间[left,right)称为一个窗口(此处不适用双闭或双开是为了避免边界问题,左闭右开初始时区间[0,0)没有元素,让right向右移动一位区间[0,1)就包含元素0了)。
滑动窗口的心法(核心思想)
维护一个窗口,不断滑动,更新答案。
详细:
-
初始化双指针left和right。【初始化】
-
先不断增加right扩大窗口,直到窗口中的内容符合要求。【寻找可行解】
-
停止增加right,不断增加left缩小窗口,直到窗口中的内容不再满足要求。在每次增加left时都要更新所求的结果。【优化可行解】
-
不断重复扩大窗口、缩小窗口的操作,直到right到达序列的末端。【滑动窗口】
模板
-
在s中使用双指针left、right,初始化left = right = 0。
-
先不断增加right扩大窗口,直到窗口中的字符串符合要求(包含p中的所有字符串)。【寻找可行解】
-
此时停止增加right,转而不断增加left缩小窗口,直到窗口中的字符串不再符合要求(不包含t中的所有字符串了)。同时,每次增加left都要更新一轮结果。【优化可行解】
-
重复第2步和第3步,直到right到达字符串的尽头。
单字符串:
void slidingWindow(string s) {
unordered_map<char, int> window;//哈希表window 记录窗口中的字符
int left=0, right=0;
while(right < s.size()){
char c=s[right];//c是将要移入窗口的字符
right++;//增大窗口
// 进行窗口内数据的一系列更新
...
// 判断左侧窗口是否要收缩
while(window needs shrink){
char d = s[left];//d是将要移出窗口的字符
left++;// 缩小窗口
// 进行窗口内数据的一系列更新
...
}
}
}
双字符串:
vector<int> findAnagrams(string s, string p) {
vector<int> res;//记录所求解
unordered_map<char,int> need;//哈希表need 记录需要凑齐的字符(一般为子串p)
unordered_map<char,int> window;//哈希表window 记录窗口中的字符 (维护一段长度为p.size()的窗口)
for(auto c:p){
//将p出现的所有元素放入哈希表need中
need[c]++;
}
int valid=0;//表示window窗口中满足need条件的字符个数,如果valid和len(need)的大小相同,则说明窗口已满足条件
int left=0,right=0;
while(right<s.size()){
char c=s[right];//c是将要移入窗口的字符
right++;//增大窗口
//进行窗口内数据的一系列更新
...
//判断左侧窗口是否要收缩(满足收缩窗口的条件)
while(window needs shrink