前言
在解决一些需要对数组进行统计、辨别、选择的一些操作的时候,如果使用多层循环嵌套去配合一些判断使用的话,虽然在某些题目上可以解决,但是时间和资源消耗过多,且容易出现未设定的错误。所以这种方法仅适用于部分简单且短小的数组进行简单的判定,故此,出现了滑动窗口这种算法。
介绍
1、在序列中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个窗口。
2、先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的序列符合要求。
3、此时,停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的序列不再符合要求。同时,每次增加 left前,都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达序列的尽头。
思路其实很简单:第 2 步相当于在寻找一个可行解,然后第 3 步在优化这个可行解,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。
适用条件
基于这个框架,遇到子串/子数组相关的题目,你只需要回答以下三个问题:
1、什么时候应该移动 right 扩大窗口?窗口加入字符时,应该更新哪些数据?
2、什么时候窗口应该暂停扩大,开始移动 left 缩小窗口?从窗口移出字符时,应该更新哪些数据?
3、什么时候应该更新结果?
只要能回答这三个问题,就说明可以使用滑动窗口技巧解题。
基本结构:(以最大字串为例)
string s;//输入的string对象也可以是数组
unordered_set<char> window;//设计一个哈希表,用来进行数据的对比和收集
int left=0,count=0;//定义左指针和一个计数
for(int right=0;right<s.size();++right){
char c=s[right];//定义一个char类型用于存放右指针所指的字符
while(window.find(c)!=window.end()){
window.erase(s[left]);//此处用于删除窗口中左侧的元素使得窗口缩小
left++;//移动左窗口边界
}
window.insert(c);//将当前右边界数据收集进哈希表
count=max(count,right-left+1);//判断最大长度
}
retuurn count;详解:
unordered_set<char> window; 此处使用了哈希表。作用方便于进行相同数据的对比。
unordered_set具体语法:
window.insert(c); 将c所代表的char字符输入给哈希表中的最后一个位置。
window.find(c); 用于在表中寻找c所指的字符,如果找到,则返回此字符所处的位置。若没找到,则返回window.end(); 一个指向表结尾的位置。
window.erase(c); 用于删除表中c所指的字符。
window.size(); 输出当前表中元素数量。
window.empty(); 判断是否为空表,若为空,则返回true,若非空则为false。
window.clear(); 移除表中所有元素,表此时为空表。
窗口体现:
初始时,定义了一个int left=0; 作为下表用于解析窗口左边界的字符值,int count=0; 则作为最后的输出。
其中外层for(int right=0;right<s.size();++right) 用于右边界的向右循环推进,扩大窗口。内层 while(window.find(c)!=window.end()) 则判断当前右边界的值是否已经出现过,若没有则跳过该循环。在每一次的窗口扩展中,先扩展右边界,同时记录该处的值.
在此处的题目要求中,我们需要找出无重复的最大字串数,则意味着,当窗口中不含重复字符时,我们需要移动右边界扩展窗口++right。而当窗口中出现重复字符时,我们要移动左边界left++;,同时移除左边界处的字符 window.erase(s[left]);缩小当前窗口,使得窗口处于一个无重复字符的情况。
当此时窗口没有重复字符时,记录当前位置差,得到字符串的长度并记录和上一次的值相比并记录最大值。
后记
说来也惭愧,自学c++这么久,也没怎么认真刷过几个题目,每次章节末的那些题目也是半翻书,半找ai写的。导致本来就不怎么强的算法思维消失的一干二净。哎,不过,随着了解的增多,我可以刷的题目也就越多,慢慢的,也希望能够积累一些自己的思维出来吧。