算法思想总结:滑动窗口算法
·
创作不易,感谢三连
一.长度最小的数组

class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums)
{
int len=INT_MAX,n=nums.size(),sum=0;//len必须要给一个很大的数,否则
for(int left=0,right=0;right<n;++right){
sum+=nums[right];//right进窗口
while(sum>=target){ //符合条件后进行更新,然后出窗口
len=min(len,right-left+1);//更新长度
sum-=nums[left++];
}
}
return len==INT_MAX?0:len;
}
};
二.无重复字符的最长字串

class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int hash[128]={};//计数
int len=0, n=s.size();
for(int left=0,right=0;right<n;++right){
++hash[s[right]];//进窗口
while(hash[s[right]]>1)
--hash[s[left++]];//出窗口
len=max(len,right-left+1);//更新长度
}
return len;
}
};
三.最大连续1的个数

class Solution {
public:
int longestOnes(vector<int>& nums, int k)
{
int len=0;
for(int left=0,right=0,zero=0;right<nums.size();++right){
if(nums[right]==0) ++zero;//进窗口
while(zero>k)
if(nums[left++]==0) --zero;//出窗口
len=max(len,right-left+1);
}
return len;
}
};
四.将x减到0的最小操作数

class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n=nums.size();
// 找到一个最长子数组和恰好为sum-x
int ret=-1;
int sum=accumulate(nums.begin(),nums.end(),0);
int target=sum-x;//目标值
if(target<0) return -1;
for(int left=0,right=0;right<n;++right){
target-=nums[right];
while(target<0) target+=nums[left++];
if(target==0) ret=max(ret,right-left+1);
}
return ret==-1?-1:n-ret;
}
};
五.水果成篮

class Solution {
public:
//最长子数组,但是不能超过两个种类
int totalFruit(vector<int>& nums) {
int n=nums.size();
int ret=1;
unordered_map<int,int> hash;//用来保存每种水果的数量
for(int left=0,right=0,kind=0;right<n;++right){
if(hash[nums[right]]++==0) ++kind;
while(kind>2)
if(--hash[nums[left++]]==0) --kind;
ret=max(ret,right-left+1);
}
return ret;
}
};
六.找到字符串种所有字母异位词

class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> ret;
int m=s.size(),n=p.size();
int hash1[26]={};//用来统计s
int hash2[26]={};//用来统计p
for(auto&e:p) ++hash2[e-'a'];
for(int left=0,right=0,count=0;right<m;++right){
char in=s[right];
if(++hash1[in-'a']<=hash2[in-'a']) ++count;//统计总字符数
//因为窗口是不变的
if(right-left+1>n){//用来维持窗口固定大小,left一定要出
char out=s[left++];
if(hash1[out-'a']--<=hash2[out-'a']) --count; //我比你小说明我是合法的
}
if(count==n) ret.emplace_back(left);
}
return ret;
}
};
七.最小覆盖字串

class Solution {
public:
string minWindow(string s, string t) {
int m=s.size(),n=t.size();
int hash1[128]={};//用来统计s的字符个数
int hash2[128]={};//用来统计t的字符个数
int kinds=0;//统计有哪些种类 该题不能统计数量,因为可能会出现很多一样的
for(auto&e:t)
if(hash2[e]++==0) ++kinds;
int begin=-1,minlen=INT_MAX;//需要记录区间
for(int left=0,right=0,count=0;right<m;++right){
char in =s[right];
if(++hash1[in]==hash2[in]) ++count;//说明凑齐这个种类了
while(kinds==count){//当种类都凑齐了说明ok了
if(right-left+1<minlen){
begin=left;
minlen=right-left+1;
}
char out=s[left++];//出窗口
if(hash1[out]--==hash2[out]) --count;//正好相等的时候说明种类少了一种
}
}
return begin==-1?"":s.substr(begin,minlen);
}
};
八.串联所有单词的子串

class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int m=words.size(),len=words[0].size();//前者是单词种类,后者是一个单词的长度(决定窗口大小)
vector<int> ret;//结果集
unordered_map<string,int> hash1;//统计words
for(auto&str:words) ++hash1[str];
for(int i=0;i<len;++i){
unordered_map<string,int> hash2;//统计s
for(int left=i,right=i,count=0;right+len-1<s.size();right+=len){
string in=s.substr(right,len);
if(++hash2[in]<=hash1[in]) ++count;
if(right-left+1>len*m){//这时候得出了
string out=s.substr(left,len);
if(hash2[out]--<=hash1[out]) --count;
left+=len;
}
if(count==m) ret.emplace_back(left);
}
}
return ret;
}
};
九.滑动窗口总结
当题目涉及到子串或者是子数组,都可以考虑到使用滑动窗口来进行解决
但是有一个需要注意的地方就是如果涉及到窗口求和的话。要保证数都是正整数,否则就不满足单调性。如下图这一题

涉及到不同的种类需要统计数量的时候,常常会用到哈希表!! (5-8题)
后面有类似题目会持续更新!!

更多推荐



所有评论(0)