基础算法:滑动窗口

目录

一、长度最小的子数组

二、无重复字符的最长子串

三、最大的连续1的个数III

四、将x减到0的最小操作数

五、水果成篮

六、找到字符串中所有字母异位词

七、串联所有单词的子串

八、最小覆盖子串


滑动窗口算法,本质是根据单调性,利用同向双指针创建窗口,然后遍历数组。

一、长度最小的子数组

209. 长度最小的子数组 - 力扣(LeetCode)

创建int变量ans,left和right首先设置在0位置,创建sum,sum+=[right],判断是否大于等于我的target,不是right++,继续判断,等到sum>=target时,right后的数据不再需要遍历,因为数组所有元素为正,遍历后边的数组只会让数据变大,由此暂停,找到第一个,记录len,len修改:只需将ans等于ans和right-left+1的min即可)sum减去【left】left可以往后遍历,判断,大于继续上述操作,小于right++,sum+【right】。即每次出窗口进窗口更新sum判断是否更新len判断是否更新ans。时间复杂度为n。

特殊情况下所有数值加起来都不够超过target,此时ans并未操作一次,判断是否等于初始值,等于返回0,不等于返回本身。

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans=INT_MAX,left=0,right=0,sum=0;
        int n=nums.size();
        while(right<n)
        {
            sum+=nums[right];       //进窗口
            while(sum>=target)      //持续 判断
            {
                ans=min(ans,right-left+1);      //直接用min即可    
                sum-=nums[left++];              //出窗口
            }
            ++right;        //结束判断,更新右窗口
        } 
        return ans==INT_MAX?0:ans;      //特殊情况处理
    }
};

二、无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode)

对于重复字符我们可以创建一个哈希表来判断

创建left,right,均从0开始,right往右遍历,【right】存进哈希表(计数桶) ,判断有没有重复元素,没有继续遍历将数据进窗口,遇到重复元素暂停,更新len,将left更新到窗口内的重复元素位置的下一个位置因为这里涉及到遍历,我们还是得按一个一个过去,(比如上图a,已经是让第一个窗口变最大的右边界,left从d到a遍历撑死都不会比这个长度还大)然后right++,判断,重复操作。

其实这题还能转化成最终返回子串,只需要在过程中多更新起始位置即可,最后return s.substr( begin,length)

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s=="")return 0;      //特殊情况直接返回提高效率
        int ans=0,left=0,right=0,hash[128]={0};//数组模拟哈希映射
        int n=s.size();
        while(right<n)
        {
            ++hash[s[right]];           //进窗口
            while(hash[s[right]]>1)
            {
                --hash[s[left++]];      //出窗口,到区间内重复元素下一位置
            }
            //写在出窗口外边,保证每一次的right操作后都能检查更新
            ans=max(ans,right-left+1);  //每次操作后都更新长度,直接max判定
            ++right;                    
        }
        return ans;
    }
};

三、最大的连续1的个数III

1004. 最大连续1的个数 III - 力扣(LeetCode)

法一:暴力+0计数器,暴力遍历同时统计0的个数,更新长度,0大于k时才截至,说明这个位置往前的最大连续1子串就这么长了.

法二:滑动窗口,基于暴力枚举的思路,就会发现这是一道很明显的滑动窗口题,特别之处在于对0的统计,创建left right,从0开始,往后遍历,if(!【right】)count+=1,更新长度,等到count>k,让while循环(count>k)让左开始回收窗口,if(!【left】)count-=1;由此大功告成,复杂度n.

cpp 复制代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n=nums.size();
        int left=0,right=0,count=0,ans=0;
        while(right<n)
        {
            if(!nums[right])++count;    //统计0
            while(count>k)
            {
                if(!nums[left++])--count;
            }
            ans=max(ans,right-left+1);      //更新长度,max自动判定
            ++right;
        }
        return ans;
    }
};

四、将x减到0的最小操作数

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

保证一段连续的子数组的和为数组元素和减去x即可

这不就是滑动窗口,不多赘述

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n=nums.size();
        int left=0,right=0,sum=0,target,ans=-1;
        for(int& e:nums){sum+=e;}
        //特殊情况处理
        if(sum<x){return -1;}
        target=sum-x;
        sum=0;
        while(right<n)
        {
            sum+=nums[right];
            while(sum>target)
            {
                sum-=nums[left++];
            }
            if(sum==target){ans=max(ans,right-left+1);}
            ++right;
        }
        return ans<0?-1:n-ans;
    }
};

五、水果成篮

904. 水果成篮 - 力扣(LeetCode)

滑动窗口,突破点就在于如何表示元素种类大于2,

思路如下:

学完map后我们知道我们可以将key代表元素种类,进窗口利用原数组【right】,并利用map的特性将value++,从而定义了一个map的存储单位,利用size函数当大于2时,while循环,先让hash.fruits.left出窗口,如果hash.fruits.left==0,也就是value为0,erase hash【nums【left++】】即可。

cpp 复制代码
class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int left=0,right=0,ans=0;
        int n=fruits.size();
        map<int,int> hash;      //便于实现种类的统计
        while(right<n)
        {
            hash[fruits[right]]++;      //进窗口的同时相当于直接创建了一个hash单位
            while(hash.size()>2)
            {
                --hash[fruits[left]];       //不着急++left
                if(hash[fruits[left]]==0)   //判断value是否为0,为0删除这个hash单位,退出while
                {
                    hash.erase(fruits[left]);
                }
                ++left;
            }
            ans=max(ans,right-left+1);
            ++right;
        }
        return ans;
    }
};

六、找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

异位词我们可以借助哈希表解决,

分析题干,即求和p一样长的区间,且区间内元素种类相同,且每个元素出现次数与之一致。创建left right=0,因为都是小写字母,创建 两个 hash【26】便于比对,利用ascii码来判断位置,我们可以创建一个变量count,count最终等于p的size,进窗口的元素如果是hash2里的,且这个数目小于hash2【该数-'a'】就说明有效,count++,right-left+1>m时要进行出窗口,出窗口判断出的元素hash1是否

count:匹配上的元素个数。

cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int hash1[26]={0},hash2[26]={0};
        //统计元素种类及其个数,方便后续判断
        for(char&e:p)
        {
            hash2[e-'a']++;
        }
        int left=0,right=0,count=0,m=p.size();
        vector<int> ans;
        while(right<s.size())
        {
            char in=s[right];
            //进窗口,维护count
            if(++hash1[in-'a']<=hash2[in-'a'])++count;
            while(right-left+1>m)
            {
                char out=s[left++];
                //出窗口,维护count
                if(hash1[out-'a']--<=hash2[out-'a'])--count;
            }
            if(count==m){ans.push_back(left);}
            ++right;
        }
        return ans;
    }
};

七、串联所有单词的子串

30. 串联所有单词的子串 - 力扣(LeetCode)

因为word里所有的string的大小都是一样的,然后要求时这个string内部顺序不变,不同string顺序可以改变,想象一下如果可以把每个string当作一个字母,这不就是上一道异位词的题目吗,将字串存进来的操作可以通过substr(pos,len)来实现。

cpp 复制代码
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        //先统计数据
        unordered_map<string,int> hash1;
        for(auto&e:words){++hash1[e];}
        int left,right,len=words[0].size(),n=s.size(),count;
        unordered_map<string,int> hash2;
        vector<int> ans;
        //遍历len次,开始位置为0,1,2...len-1
        for(int i=0;i<len;++i)
        {
            left=right=i;
            //清零!
            count=0;
            hash2.clear();
            while(right+len<=n)
            {
                string in=s.substr(right,len);
                //如果hash1里没有in,还会有一步插入key,初始value的操作
                if(hash1.count(in)&&++hash2[in]<=hash1[in])++count;
                //>=时说明hash2里多存进来了一个单词
                while(right-left>=words.size()*words[0].size())
                {
                    //substr提取子串
                    string out=s.substr(left,len);
                    if(hash1.count(out)&&hash2[out]--<=hash1[out])--count;
                    left+=len;
                }
                if(count==words.size()){ans.push_back(left);}
                right+=len;
            }
        }
        return ans;
    }
};

八、最小覆盖子串

76. 最小覆盖子串 - 力扣(LeetCode)

一样的思路,存一个begin为left即可

cpp 复制代码
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> hash1;
        for(auto&e:t){++hash1[e];}
        unordered_map<char,int> hash2;
        int left=0,right=0,count=0;
        //不要更新子串,更新子串起始位置和长度更方便
        int minlen=INT_MAX,begin=-1;
        while(right<s.size())
        {
            //进窗口,维护count
            if(hash1.count(s[right])&&++hash2[s[right]]<=hash1[s[right]])
            {++count;}
            //符合条件,析出子串
            while(count==t.size())
            {
                //如果字符不是t里的直接++left
                while(!hash1.count(s[left])){left++;}
                if(right-left+1<minlen)
                {
                    minlen=right-left+1;
                    begin=left;
                }
                //判断这个位置的left是不是有效的
                //有效->终止,寻找下一个相等的机会
                //无效->再进入循环->最终析出子串
                if(hash2[s[left]]--<=hash1[s[left]])--count;
                ++left;
            }
            ++right;
    }
    return begin==-1?"":s.substr(begin,minlen);
    }
};

此篇完。

相关推荐
Chunyyyen10 分钟前
【第二十周】自然语言处理的学习笔记05
笔记·学习·自然语言处理
ZPC821013 分钟前
opencv 获取图像中物体的坐标值
人工智能·python·算法·机器人
颇有几分姿色15 分钟前
密码学算法分类指南
算法·密码学
笨鸟笃行27 分钟前
百日挑战——单词篇(第十一天)
学习
绝无仅有36 分钟前
某游戏大厂的 Redis 面试必问题解析
后端·算法·面试
微笑尅乐38 分钟前
三种方法解开——力扣3370.仅含置位位的最小整数
python·算法·leetcode
MMjeaty40 分钟前
查找及其算法
c++·算法
寂静山林1 小时前
UVa 1597 Searching the Web
数据结构·算法
云泽8081 小时前
排序算法实战:从插入排序到希尔排序的实现与性能对决
算法·排序算法
多恩Stone2 小时前
【3DV 进阶-5】3D生成中 Inductive Bias (归纳偏置)的技术路线图
人工智能·python·算法·3d·aigc