滑動窗口算法框架與例題實踐(C++版)

首先給出算法的框架模板

/* 滑動窗⼝算法框架 */
void slidingWindow(string s, string t) {
	unordered_map<char, int> need, window;
	
	for (char c : t) 
		need[c]++;
		
	int left = 0, right = 0;
	int valid = 0;
	
	while (right < s.size()) {
		// c 是將移⼊窗⼝的字符
		char c = s[right];
		
		// 右移窗⼝
		right++;
		
		// 進⾏窗⼝內數據的⼀系列更新
		待填寫代碼處...
		
		// 判斷左側窗⼝是否要收縮
		while (valid == need.size()) {
			// d 是將移出窗⼝的字符
			char d = s[left];
			
			// 左移窗⼝
			left++;
			
			// 進⾏窗⼝內數據的⼀系列更新
			待填寫代碼處...
		}
	}
}

例題實踐:

1、leetcode 編號76. 最小覆蓋子串

題意:
給你一個字符串 S、一個字符串 T,請在字符串 S 裏面找出:包含 T 所有字符的最小子串。

示例:
輸入: S = “ADOBECODEBANC”, T = “ABC”
輸出: “BANC”

說明:
如果 S 中不存這樣的子串,則返回空字符串 “”。
如果 S 中存在這樣的子串,我們保證它是唯一的答案。

解題思路:

1、我們在字符串 S 中使⽤雙指針中的左右指針技巧,初始化 left = 0,right = 0 ,把索引左閉右開區間 [left, right) 稱爲⼀個「窗⼝」。

2、我們先不斷地增加 right 指針擴⼤窗⼝ [left, right) ,直到窗⼝中的字符串符合要求(包含了 T 中的所有字符)。

3、此時,我們停⽌增加 right ,轉⽽不斷增加 left 指針縮⼩窗⼝[left, right) ,直到窗⼝中的字符串不再符合要求(不包含 T 中的所有字符了)。同時,每次增加 left ,我們都要更新⼀輪結果。

4、重複第 2 和第 3 步,直到 right 到達字符串 S 的盡頭。這個思路其實也不難,第 2 步相當於在尋找⼀個「可⾏解」,然後第 3 步在優化這個「可⾏解」,最終找到最優解,也就是最短的覆蓋⼦串。

Ac代碼:

string minWindow(string s, string t) {
        unordered_map<int,int> need,window;

        for(int i = 0; i < t.size(); i++)
            need[t[i]]++;
        
        int left = 0, right = 0;
        int start = 0, end = INT_MAX;
        int valid = 0;

        while(right < s.size()) {
            char c = s[right];
            right++;

            //進行窗口的一系列更新
            if(need.count(c)) {
                window[c]++;
                if(need[c] == window[c]) {
                    valid++;
                }
            }
            

            while(valid == need.size()) {
                if(right-left < end) {
                    start = left;
                    end = right-left;
                }

                char d = s[left];
                left++;

                if(need.count(d)) {
                    window[d]--;
                    if(need[d] > window[d]) {
                        valid--;
                    }
                }
            }
        }

        return end == INT_MAX ? "" : s.substr(start,end);
    }


2、LeetCode 編號567. 字符串的排列

題意:
給定兩個字符串 s1 和 s2,寫一個函數來判斷 s2 是否包含 s1 的排列。
換句話說,第一個字符串的排列之一是第二個字符串的子串。

示例1:
輸入: s1 = “ab” s2 = “eidbaooo”
輸出: True
解釋: s2 包含 s1 的排列之一 (“ba”).

示例2:
輸入: s1= “ab” s2 = “eidboaoo”
輸出: False

AC代碼:

bool checkInclusion(string s1, string s2) {
        unordered_map<int,int> need,window;

        for(auto c : s1) {
            need[c]++;
        }

        int left = 0, right = 0;
        int valid = 0;

        while(right < s2.size()) {
            char c = s2[right];
            right++;

            if(need.count(c)) {
                window[c]++;
                if(need[c] == window[c])
                    valid++;
            }

            while(right-left >= s1.size()) {
                if(valid == need.size())
                    return true;
                
                char d = s2[left];
                left++;

                if(need.count(d)) {
                    if(window[d] == need[d])
                        valid--;
                    window[d]--;
                }
            }
        }

        return false;
    }


3、LeetCode 編號438. 找到字符串中所有字母異位詞

題意:
給定一個字符串 s 和一個非空字符串 p,找到 s 中所有是 p 的字母異位詞的子串,返回這些子串的起始索引。

字符串只包含小寫英文字母,並且字符串 s 和 p 的長度都不超過 20100。

說明:
字母異位詞指字母相同,但排列不同的字符串。
不考慮答案輸出的順序。

示例 1:

輸入:
s: "cbaebabacd" p: "abc"

輸出:
[0, 6]

解釋:
起始索引等於 0 的子串是 "cba", 它是 "abc" 的字母異位詞。
起始索引等於 6 的子串是 "bac", 它是 "abc" 的字母異位詞。

示例 2:

輸入:
s: "abab" p: "ab"

輸出:
[0, 1, 2]

解釋:
起始索引等於 0 的子串是 "ab", 它是 "ab" 的字母異位詞。
起始索引等於 1 的子串是 "ba", 它是 "ab" 的字母異位詞。
起始索引等於 2 的子串是 "ab", 它是 "ab" 的字母異位詞。

AC代碼:

vector<int> findAnagrams(string s, string p) {
        unordered_map<int,int> need,window;

        for(auto c : p) {
            need[c]++;
        }

        vector<int> res;

        int left = 0, right = 0;
        int valid = 0;

        while(right < s.size()) {
            char c = s[right];
            right++;

            if(need.count(c)) {
                window[c]++;
                if(need[c] == window[c]) 
                    valid++;
            }

            while(right-left >= p.size()) {
                if(valid == need.size())
                    res.push_back(left);
                
                char d = s[left];
                left++;

                if(need.count(d)){   
                    if(window[d] == need[d])
                        valid--;
                    window[d]--;
                }
            }
        }

        return res;
    }

4、LeetCode 編號3. 無重複字符的最長子串

題意:
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

示例 1:

輸入: "abcabcbb"
輸出: 3 
解釋: 因爲無重複字符的最長子串是 "abc",所以其長度爲 3

示例 2:

輸入: "bbbbb"
輸出: 1
解釋: 因爲無重複字符的最長子串是 "b",所以其長度爲 1

示例 3:

輸入: "pwwkew"
輸出: 3
解釋: 因爲無重複字符的最長子串是 "wke",所以其長度爲 3

請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。

AC代碼:

 int lengthOfLongestSubstring(string s) {
        unordered_map<int,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;
    }

後面有時間詳細說明……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章