用于自己刷题记录
转载自:https://blog.csdn.net/Dby_freedom/article/details/89066140
滑动窗口
滑动窗口是在一个小于原字符串或数组上进行操作,不在整个字符串和数组上操作,可以将一些嵌套循环变成一个单循环,减少时间复杂度。
一般用在数组和字符串中。
大体框架
滑动:窗口是移动的
窗口:窗口的大小可以是固定的,也可以不是固定,可以缩小也可扩容
思路:
- 字符串S中设置双指针,left = right =0,把[left, right]当成一个窗口
- 不断增加right指针,扩大窗口,直到窗口中字符串符合要求(包含另一个字符串的全部字符)—寻找可行解
- 此时不增加right,增加左指针,缩小窗口,如果同时满足要求,窗口大小小于最小的窗口,则更新左右区间,直到不满足要求—优化可行解
- 重复2和3,直到right到达字符串S的尽头
可行技巧
包含子串:由于一般的题目字符的限定范围一般就是26或者整个字符表128,所以设置一个定长数组,用来记录每个字符出现的次数,增加右边界,出现满足题意的情况即可,在满足的情况下更新答案并且缩小左边界,直到右边界走完整个字符串。
外加循环:一般的滑动窗口只要判断右节点是否小于给定数组的长度即可,但是对于数组里是字符串的题目,可以考虑在外面加一层大小为数组元素长度的循环。
算法实例
- 寻找最靠左子串
- Leetcode 209. 长度最小的子数组
- Leetcode 1004. 最大连续1的个数 III
- Leetcode 3. 无重复字符的最长子串
- LeetCode 76. 最小覆盖子串
- LeetCode 567. 字符串的排列
- LeetCode 239. 滑动窗口最大值
- LeetCode 30. 串联所有单词的子串
寻找最靠左子串
题目描述:寻找一个字符串中包含另一个子串的,最靠左的子串左右位置
代码示例:
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
int bcnt[10],acnt[10];//分别记录B,A中的每个字符出现的个数
string A,B;
const int INF = 0x3f3f3f3f;//定义一个无穷大值
bool check() //满足条件的check函数
{
for(int i = 0;i < 10;i++){
if(acnt[i] < bcnt[i]) return false;//A的子串中的每个字符出现的次数都大于等于B
}
return true;
}
void solve(){
cin >> A >> B;
memset(bcnt,0,sizeof(bcnt));//初始化的定义非常重要
memset(acnt,0,sizeof(acnt));//初始化的定义
for(auto& i : B) bcnt[i -'0']++; //记录B中每个字符出现的次数
int minlen = INF, left = -1, right = -1;//定义返回值,一开始默认为-1,-1返回值
int l = 0,r = 0;//[l,r),起始左右位置全为0
while(r < A.length())
{
//先处理A的子串还不包含B串所有字符的情况
//右端点需要右移, r对应的数据值得数量++
acnt[A[r++] - '0']++;
//check成立,A的子串包含B的所有字符左端点右移
while(check()){
if(r - l < minlen){//如果此时成立的最小范围长度小于目前的最小范围长度,就更新结果
minlen = r-l;
left = l,right = r-1;
}
acnt[A[l++] - '0']--; //如果成立就尝试更新结果,把左端点往右移动
}
}
printf("%d %d\n",left,right);
}
int main()
{
int T;
cin >> T;
while(T--) solve();
return 0;
}