滑动窗口算法

1.Leetcode 76.最小覆盖子串

思路:
用i,j表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1,这些长度中的最小值就是要求的结果。

我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 rr 指针,和一个用于「收缩」窗口的 ll 指针。在任意时刻,只有一个指针运动,而另一个保持静止。我们在 ss 上滑动窗口,通过移动 rr 指针不断扩张窗口。当窗口包含 tt 全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。

如何判断当前的窗口包含所有 tt 所需的字符呢?我们可以用一个哈希表表示 tt 中所有的字符以及它们的个数,用一个哈希表动态维护窗口中所有的字符以及它们的个数,如果这个动态表中包含 tt 的哈希表中的所有字符,并且对应的个数都不小于 tt 的哈希表中各个字符的个数,那么当前的窗口是「可行」的。

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> needs,window;  //needs中保存t中所有的字符,window是滑动窗口
        for(char i:t) needs[i]++;   //needs[i]如果不存在的话,会在map中创建这个键值,并且给其赋值为0
        int left=0,right=0;  //初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个窗口。
        //左闭右开区间是最方便处理的。因为这样初始化 left = right = 0 时区间 [0, 0) 中没有元素,但只要让 right 向右移动(扩大)一位,区间 [0, 1) 就包含一个元素 0 了。
        int valid=0;   //valid 变量表示窗口中满足 need 条件的字符的种类,如果 valid 和 need.size() 的大小相同,则说明窗口已满足条件,已经完全覆盖了串 T。
        int start=0,len=INT_MAX;
        while(right<s.size()){
            char c=s[right];   //c是待移入窗口的字符
            right++;
            if(needs.count(c)!=0){
                window[c]++;
                if(window[c]==needs[c]){
                    valid++;
                }
            }
            while(valid==needs.size()){
                // 在这里更新最小覆盖子串
                if((right-left)<len){
                    start=left;
                    len=right-left;
                }
                // d 是将移出窗口的字符
                char d = s[left];
                // 缩小窗口
                left++;
                // 进行窗口内数据的一系列更新
                if (needs.count(d)!=0){
                    if (window[d] == needs[d]){
                        valid--;
                    }
                    window[d]--;
                }                    
            }
        }
        return len==INT_MAX?"":s.substr(start, len);//s.substr(pos, n)返回一个string,包含s中从pos开始的n个字符的拷贝。
    }
};

2.Leetcode 567.字符串的排列

本题移动 left 缩小窗口的时机是窗口大小大于 t.size() 时,因为是排列,所以长度应该是一样的。

当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列,立即返回 true

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        unordered_map<char,int> needs,window;
        for(char i:s1) needs[i]++;
        int left=0,right=0;
        int valid=0;
        while(right<s2.size()){
            char c=s2[right];
            right++;
            if(needs.count(c)!=0){   //如果s2中的当前字符s2[right]在s1中,则把s2[right]加入到window中
                window[c]++;
                if(window[c]==needs[c]){  //如果window中的某一字符数量与s1中的该种字符数量相同,则valid++,最终判断的是valid是否等于s1.size(),用的是字符的种类进行比较
                    valid++;
                } 
            }
            while (right - left >= s1.size()) {
                // 在这里判断是否找到了合法的子串
                if (valid == needs.size())
                    return true;
                char d = s2[left];
                left++;
                // 进行窗口内数据的一系列更新
                if (needs.count(d)!=0) {
                    if (window[d] == needs[d]){
                        valid--;
                    }
                    window[d]--;
                }
            }
        }
        // 未找到符合条件的子串
        return false;
    }
};

3.Leetcode 438.找到字符串中所有字母异位词

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        unordered_map<char,int> needs,window;
        for(char i:p) needs[i]++;
        int left=0,right=0;
        int valid=0;
        
        while(right<s.size()){
            char c=s[right];
            right++;
            if(needs.count(c)!=0){
                window[c]++;
                if(window[c]==needs[c]){
                    valid++;
                }
            }
            while(right-left>=p.size()){
                if(valid==needs.size()){
                    res.push_back(left);
                }
                char d=s[left];
                left++;
                if(needs.count(d)!=0){
                    if(window[d]==needs[d]){
                        valid--;
                    }
                    window[d]--;
                }
            }
        }
        return res;
    }
};

4,Leetcode 3.无重复字符的最长字串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> window;
        int left=0,right=0;
        int res=0;
        while(right<s.size()){
            char c=s[right];
            right++;
            window[c]++;
            while(window[c]>1){   // 判断左侧窗口是否要收缩
                char d=s[left];
                left++;
                window[d]--;
            }
            res = max(res, right - left);
        }
        return res;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值