前言
在双指针的几个题型中,最难的就是滑动窗口,其实滑动窗口的思想很简单,实现起来也不难,难点就是对于边界的处理。
一、滑动窗口算法是什么,应该怎么操作?
在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 r 指针,和一个用于「收缩」窗口的 ll指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 s 上滑动窗口,通过移动 r 指针不断扩张窗口。当窗口包含 t 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。
二、例题体验
这道题目是很经典的双指针滑动窗口,对于题目的基本思路无非就是,先用一个j指针遍历数组,并且每次我们把遍历过的字符储存在数组visit标记,每次遍历的过程都判断一次,有没有出现重复字符,如果没有的话就保存最大值,如果有的话就动用i指针,把在visit数组里面的字符丢掉,到数组中不含重复字符时停止i指针,重新用j指针遍历,周而复始。
class Solution {
int visit[256];
bool Judge(){
for(int i = 0;i<256;i++){
if(visit[i]>1) return false;
}
return true;
}
public:
int lengthOfLongestSubstring(string s) {
int i = 0,j = 0,len = 0;
while(j<s.size()){
visit[s[j]]++;
if(Judge()){
len = max(len,j-i+1);
}else{
while(!Judge()){
visit[s[i]]--;
i++;
}
}
j++;
}
return len;
}
};
同类型题目
长度最小的子数组
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0,j = 0,sum = 0,len = INT_MAX;
while(j<nums.size()){
sum+=nums[j];
if(sum>=target){
while(sum>=target){
len = min(len,j-i+1);
sum-=nums[i];
i++;
}
}
j++;
}
return len==INT_MAX?0:len;
}
};
字符串的排列
这个题目有个隐藏条件,就是滑动窗口的大小其实是固定的,因此同时动i,j指针即可
class Solution {
public:
int cnt[26],visit[26];
bool Judge()
{
for(int i = 0;i<26;i++){
if(cnt[i]!=visit[i]) return false;
}
return true;
}
bool checkInclusion(string s1, string s2) {
if(s1.size()>s2.size()) return false;
for(int i = 0;i<s1.size();i++){
cnt[s1[i]-'a']++;
visit[s2[i]-'a']++;
}
int i = 0,j = s1.size();
while(j<s2.size()){
if(Judge()) return true;
visit[s2[j]-'a']++;
visit[s2[i]-'a']--;
i++;
j++;
}
if(Judge()) return true;
return false;
}
};
2.较为更难的滑动窗口
最小区间
这个题目较比之前多了点难度,给出的数据让我们难以直接遍历查找。所以就需要先进行预处理,对每个数据都先标记为该数组的编号,然后把数据大小按顺序排序好,这样我们就可以按照上面滑动窗口的做法直接ac。
class Solution {
int visit[3555];
bool Judge(vector<vector<int>>& nums){
for(int i = 0;i<nums.size();i++){
if(!visit[i]) return false;
}
return true;
}
public:
vector<int> smallestRange(vector<vector<int>>& nums) {
vector<pair<int,int>> v;
vector<int> ans;
for(int i = 0;i<nums.size();i++){
for(int j = 0;j<nums[i].size();j++){
v.push_back({nums[i][j],i});
}
}
sort(v.begin(),v.end());
int i = 0,j = 0,len = 0x3f3f3f3f,l = 0,r = 0;
while(j<v.size()){
visit[v[j].second]++;
while(Judge(nums)){
if(len>v[j].first-v[i].first){
len = v[j].first-v[i].first;
l = v[i].first,r = v[j].first;
}
visit[v[i].second]--;
i++;
}
j++;
}
ans.push_back(l);
ans.push_back(r);
return ans;
}
};
训练题目
删除字母匹配字符
class Solution {
bool Judge(string s1,string s2){
int i = 0,j = 0;
while(j<s2.size()){
if(s1[i]==s2[j]){
i++;
if(i>=s1.size()) return true;
}
j++;
}
return false;
}
public:
string findLongestWord(string s, vector<string>& dictionary) {
vector<string> v;
int maxlen = 0;
for(int i = 0;i<dictionary.size();i++){
bool flag = Judge(dictionary[i],s);
if(flag){
if(maxlen<dictionary[i].size())
maxlen = dictionary[i].size();
v.push_back(dictionary[i]);
}else continue;
}
if(v.size()==0) return "";
else{
sort(v.begin(),v.end());
for(int i = 0;i<v.size();i++){
if(v[i].size()==maxlen){
return v[i];
}else continue;
}
return v[0];
}
}
};
总结
本文总结了滑动窗口的一些用法和延伸,滑动窗口一般普通题目难度不大,思路都比较清晰,多多练习就能掌握,希望能帮助到大家。