滑動窗口問題_總結

一. 一般需要用到雙指針, 還有特殊的需要用到特定數據結構sorted_map. 

二. 這類題目脫離不開主串(主數組)和子串(子數組)的關係, 短的在長的中滿足一定的條件.要求時間O(n),空間O(1).

三. 雙指針確定一個窗口.

四. 思路是每次保證右指針往前移動一格, 每次移動會有新的元素進入窗口. 條件可能發生改變, 根據這個決定左指針是否移動.

五. 例題

1.  leetcode76_最小覆蓋子串 

2.  暴力法: 列舉出S所有的子串, 並且判斷子串是否包含有T全部字母,最後選出長度最小的子串

3. 我們的做法是, 先找到可行解,即right指針先走,直到包含所有的T字母, 然後再優化可行解, 移動left指針, 窗口不斷向右滑動, 最終找到最優解.

4. 假設needs 和 window 相當於計數器,分別記錄 T 中字符出現次數和窗口中的相應字符的出現次數。用兩個哈希表來解決.

class Solution {
public:
    string minWindow(string s, string t) {
        //初始情況
        if(t.length()==0 || s.length()<t.length()) return "";
        //雙指針
        int left=0, right=0;
        int match = 0;
        map<char, int> need;
        map<char, int> window;
        //設置沒有匹配的字符串的情況
        int start=0, minLength=INT_MAX;
        //首先哈希表列出t的字符個數情況.
        for(int i=0;i<t.length();i++) {
            need[t[i]]++;
        }
        //然後指定雙指針,right直到s末尾才停止.
        while(right<s.length()) {
            //如果s的新增的字符在t中,則window對應的字符++,
            //如果第一次到了和t字符相同的個數,則match++.
            if(need.count(s[right])) {
                window[s[right]]++;
                if(window[s[right]]==need[s[right]]) {
                    match++;
                }
            }
            right++;
            //當t中字符在s中全部出現時,可行解滿足,
            接下來優化可行解.
            while(match==need.size()) {
                //字符串長度越小則保留.
                if(right-left<minLength) {
                    start = left;
                    minLength = right-left;
                }
                //如果移動left時刪除的字符在t中,
                //則如果個數達到need臨界,則match--,
                //其他情況window對應字符--.
                if(need.count(s[left])) {
                    if(window[s[left]]==need[s[left]]) match--;
                    window[s[left]]--;
                }
                left++;
            }
            //別忘了left++和right++, 雙指針自身也需要移動.
        }
        return (minLength==INT_MAX) ? "" : s.substr(start, minLength); 
    }
};

5. 算法時間複雜度O(M+N). 兩個while循環最多執行2M次, 因爲while的次數就是left和right走的路程, 因爲滑動窗口, left和right都是++,即都是前進,因此走的路程最多是2M, 即兩個s字符串的長度.

二. leetcode438_找到字符串中所有字母異位詞.

1. 和上一題思路一樣, 套用模板, 先找到s中包含所有字母的字符串, 然後再去驗證長度是否相同, 再添加進最終的結果.

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        if(s.length()==0 || s.length() < p.length()) return res;
        int left=0, right=0;
        map<char, int> need;
        map<char, int> window;
        int match = 0;
        for(int i=0; i<p.length(); i++) {
            need[p[i]]++;
        }
        while(right<s.length()) {
            //先找到字母個數符合條件的.
            if(need.count(s[right])) {
                window[s[right]]++;
                if(window[s[right]]==need[s[right]]) match++;
            }
            right++;
            //然後再篩選出個數符合要求的添加進最終的結果.
            while(match==need.size()) {
                if(right-left == p.size()) {
                    res.push_back(left);
                }
                if(need.count(s[left])) {
                    window[s[left]]--;
                    if(window[s[left]]<need[s[left]]) match--;
                }
                left++;
            }   
        }
        return res;
    }
};

2. 時間同樣爲O(N+M).

三. leetcode3_無重複字符的最長子串.

1. 思路沒變,但是代碼結構有些變化.

2. 遇到子串問題, 首先想到的就是滑動窗口技巧.

3. 先移動right, 等出現重複字符的時候, 再向左移動left, 如此往復.

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //初始條件
        if(s.length()==0) return 0;
        int left = 0, right = 0;
        map<char, int> window;
        int match = 0;
        int res = INT_MIN;
        //當滿足沒有重複字符的時候,right指針一直向右移動,
        //並且只有字符個數爲1時才match++
        while(right < s.length()) {
            window[s[right]]++;
            if(window[s[right]]==1) match++;
            right++;
            //當不滿足無重複子串時,需要移動左指針
            while(right-left!=match) {
                //如果不等於,則說明移動會使無重複子串的長度減1.
                if(s[left]!=s[right-1]) { 
                    match--;
                }
                window[s[left]]--;
                left++;
            }
            //找到最長的子串
            res = max(res, match);
        }
        return res;
    }
};

發佈了95 篇原創文章 · 獲贊 26 · 訪問量 2122
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章