LeetCode hot100@滑动窗口&子串

滑动窗口大致逻辑:

int left = 0, right = 0;

while (right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;
    
    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }
}

滑动窗口算法框架:

void slidingWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (const char &c : t) need[c]++;
 
    int left = 0, right = 0;
    int valid = 0; 
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);
        /********************/
 
        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

套模板时考虑以下四个问题:

  • 当移动 right 扩大窗口,即加入字符时,应该更新哪些数据?
  • 什么条件下,窗口应该暂停扩大,开始移动 left 缩小窗口?
  • 当移动 left 缩小窗口,即移出字符时,应该更新哪些数据?
  • 我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?

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

暴力:用map维护窗口内的字符不重复,每次重新计数只让左边界+1

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(!s.size()) return 0;

        unordered_map<char, int> mp;
        int left = 0, right = 1;
        int res = 1;

        mp[s[0]] = 1;
        while(left < right && right < s.size()) {
            if(mp[s[right]] != 1) {
                mp[s[right]] = 1;
                res = max(res, right - left + 1);
                right++; 
                // printf("res = %d right = %d left = %d\n", res, right, left);
            } else {
                left++;
                mp.clear();
                mp[s[left]] = 1;
                right = left + 1;
            }
            
        }

        return res; 
    }
};

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

用substr逐步构造子串与p比较,超时

滑动窗口

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        unordered_map<char, int> need, window;
        for(const auto &c : p) {
            need[c]++;
        }
        
        int left = 0, right = 0;
        int vaild = 0;
        while(right < s.size()) {
            //右窗口更新
            char rc = s[right++];
            if(need.count(rc)) {
                window[rc]++;
                if(need[rc] == window[rc]) vaild++;
            }
       
            //判断左窗口更新
            while(right - left >= p.size()) {
                if(vaild == need.size()) { //注意是need的长度而不是p
                    res.push_back(left);
                }
                char lc = s[left++];
                if(need.count(lc)) {
                    if(need[lc] == window[lc]) vaild--;
                    window[lc]--;
                }
            }
        }

        return res; 
    }
};

560. 和为 K 的子数组

*以为是滑动窗口(因为有负数不好处理),实则是前缀和*

*+哈希(相当于1. 两数之和)*

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res = 0;
        unordered_map<int, int> mp;
        vector<int> preSum;
        preSum.resize(nums.size() + 1);

        preSum[0] = 0; //注意要空一个0,否则会漏掉示例1的第一个子数组或者自身一个数就满足的情况
        for(int i = 0; i < nums.size(); i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }

        for(int i = 0; i < preSum.size(); i++) {
            cout << preSum[i] << " ";
            int cnt = mp[preSum[i] - k];
            if(cnt) {
                res += cnt;
                // cout << "->yes ";
            }
            mp[preSum[i]]++;  
        }
         
        return res;
    }
};

239. 滑动窗口最大值

自定义单调队列

class Solution {
private:
    class MyQueue {
    public:
        deque<int> dq;
        void pop(int x) {
            if(!dq.empty() && dq.front() == x) dq.pop_front();
        }
        void push(int x) {
            while(!dq.empty() && x > dq.back()) dq.pop_back();     
            dq.push_back(x);
        }
        int front() {
            return dq.front();
        }
    };
public:   
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        MyQueue que;
        for(int i = 0; i < k; i++) que.push(nums[i]);
        res.push_back(que.front());
        for(int i = k; i < nums.size(); i++) {
            que.pop(nums[i-k]);
            que.push(nums[i]);
            res.push_back(que.front());
        }
        
        return res;
    }
};

76. 最小覆盖子串

滑动窗口经典题

class Solution {
public:
    string minWindow(string s, string t) {
        // key是字符,value是个数;need是t的字符,window是当前窗口的字符
        unordered_map<char, int> need, window;
        for(auto &c : t) need[c]++;

        int left = 0;
        int valid = 0;
        string res;
        // 最好不要用while循环,因为right是在每次循环最后,即统计完之后才++
        for(int right = 0; right < s.size(); right++) {
            // 更新右边界
            char ch = s[right];
            window[ch]++;
            // window[ch]至少为1,如果window[ch] <= need[ch],说明ch是需要的字符(属于t)
            if(window[ch] <= need[ch]) { 
                valid++;
            }
            // 更新左边界
            while(window[s[left]] > need[s[left]]) { // 如果左边界字符多余了
                window[s[left]]--;
                left++;
            }
            // 已经覆盖
            if(valid == t.size()) {
                if(res.size() == 0 || right - left + 1 < res.size()) {
                    res = s.substr(left, right - left + 1);
                }
            }
            // 不用把valid重置为0,只需等右边界找到替代品,左边界自然会缩,res就会更新
        }
        return res;
    }
};


不准投币喔 👆

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇