滑动窗口总结

滑动窗口大致思路

1.我们使用双指针的思路,初始定义两个指针,left=right=0,把索引闭区间[left,right]看作一个滑动窗口

2.然后不断增加right的值,直到窗口中的字符串符合要求

3.然后,停止增加right的值,转而增加left的值,直到窗口中的字符串不符合要求,每次增加left,就要更新一轮结果。

4.不断重复2,3步骤,直到遍历完整个字符串

通俗来讲,第2步是为了找到可行解,然后第三步是为了“优化可行解”。

滑动窗口的窗口大小可能固定,也可能不固定。

窗口大小固定代码模板:

// 固定窗口大小为 k
    string s;
    // 在 s 中寻找窗口大小为 k 时的所包含最大元音字母个数
    int  right = 0;
    while(right < s.size()) {
        window.add(s[right]);
        right++;
        // 如果符合要求,说明窗口构造完成,
        if (right>=k) {
            // 这是已经是一个窗口了,根据条件做一些事情
           // ... 可以计算窗口最大值等 
            // 最后不要忘记把 right -k 位置元素从窗口里面移除
        }
    }
    return res;

窗口大小不固定代码模板:

    string s, t;
        // 在 s 中寻找 t 的「最小覆盖子串」
    int left = 0, right = 0;
    string res = s;

    while(right < s.size()) {
        window.add(s[right]);
        right++;
        // 如果符合要求,说明窗口构造完成,移动 left 缩小窗口
        while (window 符合要求) {
            // 如果这个窗口的子串更短,则更新 res
            res = minLen(res, window);
            window.remove(s[left]);
            left++;
        }
    }
    return res;

例题:

  1. 无重复字符的最长子串(https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char>res;
        int n = s.length();
        int rk=-1,ans=0;
        for(int i=0;i<n;i++){
            if(i!=0){
                res.erase(s[i-1]);
            }
            while(rk+1<n && ! res.count(s[rk+1])){
                rk++;
                res.insert(s[rk]);
            }
            ans=max(ans,rk-i+1);
        }
        return ans;
    }
};

  1. 串联所有单词的子串(https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int>ans;
        if(s.length() == 0 || words.size() == 0) return ans;
        int n = s.length();//字符串长度
        int len = words[0].length();//一个单词的长度
        int count = words.size();//单词个数
        int sum_len = count*len;//单词总长度
        unordered_map<string,int>mp; //统计每个单词出现的次数
        for(int i=0;i<words.size();i++){
            mp[words[i]]++;
        }
        for(int i=0;i<len;i++){
            int left=i,right=i;
            unordered_map<string,int>tmap;
            int cnt = 0;
            while(right+len<=n){ //截取是从right开始,故是小于等于
                string tmp = s.substr(right,len);
                right+=len;
                if(mp.count(tmp)==0){ //若该单词未出现过
                    cnt=0;
                    left = right;
                    tmap.clear();
                }
                else { //单词出现过
                    tmap[tmp]++;
                    cnt++;
                    while(tmap[tmp]>mp[tmp]){ //重点部分,若该单词次数超了,说明在(left->right-len)之间一定有个字符串和tmp相等,因此从left开始截取字符串,舍掉超出的那部分
                        string t=s.substr(left,len);
                        tmap[t]--;
                        cnt--;
                        left+=len;
                    }
                    if(cnt == count) ans.push_back(left);
                }
                
            }
        }
        return ans;
    }
};
七月在野,八月在宇,九月在户,十月蟋蟀入我床下
原文地址:https://www.cnblogs.com/voids5/p/14352912.html