前言
👧个人主页:@小沈YO.
😚小编介绍:欢迎来到我的乱七八糟小星球🌝
📋专栏:优选算法
🔑本章内容:滑动窗口
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~
提示:以下是本篇文章正文内容,下面案例可供参考
一、滑动窗口示例:
1.1 ⽔果成篮
-
题⽬链接:904. ⽔果成篮
-
题⽬描述:
-
解法(滑动窗⼝):
算法思路:研究的对象是⼀段连续的区间,可以使⽤「滑动窗⼝」思想来解决问题。让滑动窗⼝满⾜:窗⼝内⽔果的种类只有两种。 -
做法:右端⽔果进⼊窗⼝的时候,⽤哈希表统计这个⽔果的频次。这个⽔果进来后,判断哈希表的⼤⼩:
- 如果⼤⼩超过 2:说明窗⼝内⽔果种类超过了两种。那么就从左侧开始依次将⽔果划出窗⼝,直到哈希表的⼤⼩⼩于等于 2,然后更新结果;
- 如果没有超过 2,说明当前窗⼝内⽔果的种类不超过两种,直接更新结果 ret。
- 算法流程:
- 初始化哈希表 hash 来统计窗⼝内⽔果的种类和数量;
- 初始化变量:左右指针 left = 0,right = 0,记录结果的变量 ret = 0;
- 当 right ⼩于数组⼤⼩的时候,⼀直执⾏下列循环:
<1> 将当前⽔果放⼊哈希表中;
<2> 判断当前⽔果进来后,哈希表的⼤⼩: 如果超过 2:
◦ 将左侧元素滑出窗⼝,并且在哈希表中将该元素的频次减⼀;
◦ 如果这个元素的频次减⼀之后变成了 0,就把该元素从哈希表中删除;
◦ 重复上述两个过程,直到哈希表中的⼤⼩不超过 2;
<3> 更新结果 ;
<4> right++,让下⼀个元素进⼊窗⼝; - 循环结束后, 存的就是最终结果
- C++代码(数组模拟哈希/容器)
class Solution {
public:
//const int N=1e5+10;
int totalFruit(vector<int>& fruits)
{
int cnt=0;
//int hash[N];
//memset(hash,0,sizeof(hash));
unordered_map<int,int>hash;
int left,right,ans=0;
for(left=0,right=0;right<fruits.size();right++)
{
// if(hash[fruits[right]]==0)
// {
// cnt++;
// }
hash[fruits[right]]++;//进窗口
//while(cnt>2)
while(hash.size()>2)//判断
{
if(--hash[fruits[left]]==0)
{
//cnt--;
hash.erase(fruits[left]);
}
left++;//出窗口
}
ans=max(ans,right-left+1);//更新结果
}
return ans;
}
};
1.2 找到字符串中所有字⺟异位词
- 题⽬链接:438. 找到字符串中所有字⺟异位词
- 题⽬描述:
- 解法(滑动窗⼝ + 哈希表):
算法思路:注意分清cnt代表的是什么,right-left+1代表的是什么 ,还有p.size()
- 因为字符串 p 的异位词的⻓度⼀定与字符串 p 的⻓度相同,所以我们可以在字符串 s 中构造⼀个⻓度为与字符串 p 的⻓度相同的滑动窗⼝,并在滑动中维护窗⼝中每种字⺟的数量;
- 当窗⼝中每种字⺟的数量与字符串 p 中每种字⺟的数量相同时,则说明当前窗⼝为字符串 p 的异位词;
- 因此可以⽤两个⼤⼩为 26 的数组来模拟哈希表,⼀个来保存 s 中的⼦串每个字符出现的个数,另⼀个来保存 p 中每⼀个字符出现的个数。这样就能判断两个串是否是异位词。
- C++代码
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
int hash1[26]={0},hash2[26]={0};
vector<int> v;
for(auto&e:p)
hash1[e-'a']++;
int left,right,cnt=0;//cnt代表窗口中有效值的个数
for(left=0,right=0;right<s.size();right++)
{
char in=s[right];
//这里是每个字符都进入窗口但是不存在于p中的字符++会大于hash1中该字符的数量所以只有<=才将有效值cnt++
if(++hash2[in-'a']<=hash1[in-'a'])cnt++;//进窗口+(有效值++)
if(right-left+1>p.size())//窗口大小大于p的个数就要出窗口
{
char out=s[left++];
//这条语句代表的是:当出窗口的字符个数<=has1中该字符的个数时,
//就代表出的是一个有效字符然后将该字符--,窗口中有效值count--
if(hash2[out-'a']--<=hash1[out-'a'])cnt--;
}
if(cnt==p.size())//更新结果
v.push_back(left);
}
return v;
}
};
1.3 串联所有单词的⼦串
- 题⽬链接:30. 串联所有单词的⼦串
- 题⽬描述:
- 算法思路:这道题和上一道类似只不过是字符换成了字符串
如果我们把每⼀个单词看成⼀个⼀个字⺟,问题就变成了找到「字符串中所有的字⺟异位词」。⽆⾮就是之前处理的对象是⼀个⼀个的字符,我们这⾥处理的对象是⼀个⼀个的单词 - C++代码
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = words.size(), m = words[0].size();
int left, right,cnt = 0;
unordered_map<string, int> hash1, hash2;
for (auto& e : words)
hash1[e]++;
vector<int> v;
for(int i=0;i<m;i++)//这里刚开始没有加理解错了以为s都是和words相同长度的字符串
{
hash2.clear();cnt=0;
for (left = i, right = i; right+m <= s.size(); right+=m)
{
string instr=s.substr(right,m);
if (++hash2[instr] <= hash1[instr])cnt++;
if ((right - left + 1) > n*m)
//这里不能用/因为right-left+1并不一定是一个可以除尽m的数就像7/3和2比本来该出窗口但是2==2就跳过了
{
string outstr = s.substr(left, m);
left += m;
if (hash2[outstr]-- <= hash1[outstr])cnt--;
}
if (cnt == n)
v.push_back(left);
}
}
return v;
}
};
1.4 最⼩覆盖⼦串
- 题⽬链接:76. 最⼩覆盖⼦串
- 题⽬描述:
- 解法(滑动窗⼝ + 哈希表):
算法思路:
- 研究对象是连续的区间,因此可以尝试使⽤滑动窗⼝的思想来解决。
- 如何判断当前窗⼝内的所有字符是符合要求的呢?
我们可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝内字符串的信息。当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案。
- 算法流程:
- 定义两个全局的哈希表:
- 先将 t 的信息放⼊ 1 号哈希表中;
- 初始化⼀些变量:左右指针: left = 0,right = 0 ;⽬标⼦串的⻓度: ans=INT_MAX ;⽬标⼦串的起始位置: begin ;(通过⽬标⼦串的起始位置和⻓度,我们就能找到结果)
- 当 right ⼩于字符串 s 的⻓度时,⼀直下列循环:
<1> 将当前遍历到的元素扔进 2 号哈希表中;
<2> 检测当前窗⼝是否满⾜条件:
如果满⾜条件:
◦ 判断当前窗⼝是否变⼩。如果变⼩:更新⻓度 ans,以及字符串的起始位置 begin;
◦ 判断完毕后,将左侧元素滑出窗⼝,顺便更新 2 号哈希表;
- C++代码
class Solution {
public:
string minWindow(string s, string t)
{
unordered_map<char,int>hash1,hash2;
int left,right,cnt=0;//cnt代表字符串中有效种类数
int ans=INT_MAX,begin=-1;
//set去重求t的种类数
set<char> st;
for(auto&e:t)st.insert(e);
for(auto&e:t)
hash1[e]++;
for(left=0,right=0;right<s.size();right++)
{
char instr=s[right];
if(++hash2[instr]==hash1[instr])cnt++;
while(cnt==st.size())
{
if(ans>right-left+1)
{
ans=right-left+1;
begin=left;
}
if(hash2[s[left]]--==hash1[s[left]])cnt--;
left++;
}
}
return begin==-1?"":s.substr(begin,ans);
}
};
二、滑动窗口总结:
- 根据上面几道例题可以看出滑动窗口大多和hash表配合使用除此之外还要注意使用滑动窗口的的条件:一定是连续的区间才可以使用滑动窗口
- 除此之外,还要注意滑动窗口更新结果的位置具体题目具体分析