滑动窗口~~

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


介绍

具体来说,我们可以使用两个指针 l e f t left left r i g h t right right 分别表示滑动窗口的左右边界,然后通过不断移动右指针 r i g h t right right 来扩大窗口,同时根据问题的要求调整左指针 l e f t left left 来缩小窗口。当右指针 r i g h t right right 扫描到字符串或数组的末尾时,算法的执行就完成了。
由于区间连续,因此当区间发生变化时,可以通过旧有的计算结果对搜索空间进行剪枝,这样便减少了重复计算,降低了时间复杂度

题目

1.无重复字符的最长子串


https://leetcode.cn/problems/wtcaE1/description/

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int l=0,r=0,n=s.length();
        int ans=0;
        while(r<n){
            for(int i=r-1;i>=l;i--){
                if(s[i]==s[r]){
                    l=i+1;
                    break;
                }
            }
            ans=max(ans,r-l+1);
            r++;
        }
        return ans;
    }
};

2.最小覆盖子串


https://leetcode.cn/problems/M1oyTv/


class Solution {
public:
    string minWindow(string s, string t) {
        int len_s = s.length(), len_t = t.length();
        long long int status = 0;
        int t1[60], now[60];
        memset(t1, 0, sizeof t1);
        memset(now, 0, sizeof now);
        for (int i = 0; i < len_t; i++) {
            int idx = (t[i] - 'A');
            if (!t1[idx])status ^=((long long)1 << idx);
            t1[idx]++;
        }
        string ans;
        int fl, fr;
        int cnt = 0;
        int l = 0, r = 0;
        for (int i = 0; i < len_s; i++) {
            int idx = (s[i] - 'A') ;
            now[idx]++;
            if ((status >> idx) & 1 && t1[idx] <= now[idx])status ^= ((long long)1 << idx);
            if (!status) {
                cnt = i - l + 1;
                r = i;
                fl = l, fr = r;
                break;
            }
        }
        if (status)return "";
        while (r < len_s) {
            int idx = (s[l] - 'A') ;
            while (t1[idx] == 0 || now[idx] > t1[idx]) {
                if (t1[idx] != 0)now[idx]--;
                l++;
                idx = (s[l] - 'A') ;
            }
            if (r - l + 1 < cnt) {
                cnt = r - l + 1;
                fl = l, fr = r;
            }
            r++;
            int idxr = (s[r] - 'A') ;
            if (r!=len_s && t1[idxr])now[idxr]++;
        }
        ans.assign(s, fl, cnt);
        return ans;
    }
};

3.加油站


https://leetcode.cn/problems/gas-station/description/

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n=gas.size();
        vector<int> chge(n+1);
        for(int i=0;i<n;i++){
            chge[i]=gas[i]-cost[i];
        }
        int sum=0;
        for(int l=0,r=0;;r=(r+1)%n){
            sum+=chge[r];
            if(sum>=0){
                if((r-l+n)%n==n-1)return l;
                continue;
            }
            while(sum<0){
                sum-=chge[l];
                l=l+1;
                if(l==n)return -1;
            }
        }
        return -1;
    }
};

4.替换子串得到平衡字符串


https://leetcode.cn/problems/replace-the-substring-for-balanced-string/description/

class Solution {
public:
    int balancedString(string s) {
        int cnt[90];
        int n=s.length();
        int want=n/4;
        int len=1e5;
        for(char c:s){
            cnt[c]++;
        }
        if(cnt['Q']==want && cnt['W']==want && cnt['E']==want && cnt['R']==want)return 0;
        for(int l=0,r=0;r<n;r++){
            cnt[s[r]]--;
            if(abs(cnt['Q']-want)+abs(cnt['W']-want)+abs(cnt['E']-want)+abs(cnt['R']-want)!=r-l+1){
                continue;
            }
            while(abs(cnt['Q']-want)+abs(cnt['W']-want)+abs(cnt['E']-want)+abs(cnt['R']-want)==r-l+1){
                cnt[s[l]]++;
                l++;
            }
            if(r-l+2<len){
                len=r-l+2;
            }
        }
        return len;
    }
};

5.k个不同整数的子数组


https://leetcode.cn/problems/subarrays-with-k-different-integers/

class Solution {
public:
    int subarraysWithKDistinct(vector<int>& nums, int k) {
        int sum1=0,sum2=0;
        int n=nums.size();
        int cnt[20003];
        memset(cnt,0,sizeof cnt);
        int now=0;
        
        for(int l=0,r=0;r<n;r++){
            if(cnt[nums[r]]==0)now++;
            cnt[nums[r]]++;
            while(now>k){
                cnt[nums[l]]--;
                if(cnt[nums[l]]==0)now--;
                l++;
            }
            if(now<=k)sum1+=r-l+1;
        }
        if(k==1)return sum1;
        memset(cnt,0,sizeof cnt);
        now=0;
        for(int l=0,r=0;r<n;r++){
            if(cnt[nums[r]]==0)now++;
            cnt[nums[r]]++;
            while(now>k-1){
                cnt[nums[l]]--;
                if(cnt[nums[l]]==0)now--;
                l++;
            }
            if(now<=k-1)sum2+=r-l+1;
        }
        return sum1-sum2;
    }
    
};

6.至少有k个重复字符的最长子串

在这里插入图片描述


https://leetcode.cn/problems/longest-substring-with-at-least-k-repeating-characters/description/

class Solution {
public:
    int longestSubstring(string s, int k) {
        int n=s.length();
        int ans=0;
        int cnt[256];
        for(int want=1;want<=26;want++){
            memset(cnt,0,sizeof cnt);
            for(int l=0,r=0,collect=0,satify=0;r<n;r++){
                if(cnt[s[r]]==0)collect++;
                cnt[s[r]]++;
                if(cnt[s[r]]==k)satify++;
                while(collect>want){
                    if(cnt[s[l]]==k)satify--;
                    cnt[s[l]]--;
                    if(cnt[s[l]]==0)collect--;
                    l++;
                }
                if(satify==want){
                    ans=max(r-l+1,ans);
                }
            }
        }
        return ans;
    }
};

总结

单调性与范围相关
滑动窗口的逻辑带入到题目中,看是否合理

  • 随着r增加,从不满足到满足,再调整l,又变为不满足,这种往往对应最小长度
  • 随着r增加,从满足到不满足,再调整l,又变为满足,这种往往对应最大长度(或满足数目)
    最后一题也就是第6题有些许特殊:通过技巧将题目翻译成只能含有i(1~26)种字符,并且每种字符数量大于等于k个的最长长度,最后取max;
    翻译过后有两个限制条件,然后因为求的是最长子串,那么对应第二种,即在限制条件下,从满足到不满足。显然“大于等于k个”的限制条件不符合,而‘’只能含有i种字符”的条件好像符合,又好像不符合,为什么这样说呢,因为我们发现当i!=1的时候,随着r从0递增,子串是从不满足到满足然后最后到不满足的,既然如此,那能不能用滑动窗口呢,答案是肯定的,因为在判断是否符合语句中不满足不会进入判断,就没事啦~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值