Longest Substring Without Repeating Characters--------採用滑動窗口SlidingWindow解題總結

1.可以用滑動窗口法來解決的問題一般爲 滿足某種條件的最大子數組,最長子字符串,總之就是連續的最長的,(也可以是最小的,還沒做過,之後補充)

通俗點,滑動窗口就是拿一個可以變寬變窄的框框,在一個字符串上進行移動,當然怎麼移動就需要根據問題來具體說明,直到窗口內或者窗口外符合題目要求即可(一般是關注窗口內,當然也有關注窗口外的,下面有具體例子)

而且滑動窗口題目一般與哈希set或哈希map結合使用

寫一個這種題目的簡單框架;

1.定義窗口邊界

int left = 0;

int right = 0;

2.定義哈希表,用於存儲窗口內的元素或者元素與其下標

 unordered_map 或者 unordered_set

3.開始移動窗口,一般遵循以下原則

先for循環移動右窗口,隨着窗口的擴大,可能不再滿足條件,不再滿足條件時一般需要相應的記錄(一般爲最終問題的中間可能結果),此時我們通過while循環來移動窗口左邊界,直到窗口內元素又滿足條件爲止,然後繼續執行未執行完的for循環,即繼續移動右窗口

當然,這只是一個簡單的框架,具體問題還是要具體分析來解決的

趁熱打鐵,我們基於框架套幾題

說明:該題要求我們求無重複字符的最長子字符串,很顯然我們可以通過滑動窗口來進行尋找,只要判斷窗口內是否滿足無重複字符並記錄其長度即可

class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        //定義變量記錄最終結果
        int res = 0;
        //構造出窗口
        int left = 0;
        int right = 0;
        //用哈希set來記錄窗口內元素
        unordered_set<int> set;
        //開始用for循環移動窗口右邊界
        for(right = 0; right < s.size(); right++)
        {
            //先滑動右窗口,並隨時記錄窗口內元素,直至map中有兩個相同元素
            if(set.find(s[right]) == set.end())
            {
                set.insert(s[right]);
            }
            else
            {
                //此時若再滑動右窗口,則會出現兩個元素,不滿足題目的無重複元素要求,
                //所以通過while循環來進行左窗口的移動,直至繼續移動右窗口時不再出現重複元素
                 
                while(set.find(s[right]) != set.end())
                {
                    //記錄可能的最長無重複字符的子數組
                    res = max(res,right-left);
                    set.erase(s[left]);
                    left++;
                }
                set.insert(s[right]);
            }

        }
        //爲防止不用移動左邊界的情況,需要最後再進行一次比較
        res = max(res,right-left);
        return res;
    }
};

 

第二題:Longest Substring with At Most Two Distinct Charachers

Given a string s, find the length of the longest sunstring t that contains at most 2 distinct characters.

即 找到最多包含2個不同字符串的最長子字符串

題目要求我們 找到最長的子字符串,且該子字符串內最多隻能包括兩個不同的字符,求這樣的子字符串的最大長度

同樣的,可以用滑動窗口法來解決,關注窗口內的字符串即可

class Solution {
public:
    int lengthOfLongestSubstringTwoDistinct(string s) 
    {
        int res = 0;
        //定義窗口
        int left = 0;
        int right = 0;
        //用哈希map來記錄窗口不同元素以及各個元素的數量
        unordered_map<char, int> map;
        for (right = 0; right < s.size(); right++) 
        {
            map[s[right]]++;
            
            while(map.size() > 2)//說明map中有三個不同元素了,不滿足窗口條件了,需要移動左窗口
            {
                res = max(res, right - left);
                if (map[s[left]]-- == 0) //說明其中某一元素以已經沒有了,可以繼續移動右窗口了
                {
                    map.erase(s[left]);
                }
                ++left;
            }
            
        }
        res = max(res, right-left);
        return res;
    }
};

第三題:Longest Repeating Character Replacement

Given a string s that consists of only uppercase English letters, you can perform at most k operations on that string.

In one operation, you can choose any character of the string and change it to any other uppercase English character.

Find the length of the longest sub-string containing all repeating letters you can get after performing the above operations.

即 給定一個只包含大寫字母的字符串,你可以對其進行k次操作,每一次操作再你可以替換任意一個字符爲另外任意一個字符,那麼你可以得到的具有相同字母的最長子字符串是多長?

Example 1:

Input: s = "ABAB", k = 2

Output: 4

Explanation: Replace the two 'A's with two 'B's or vice versa.

Example 2:

Input:
s = "AABABBA", k = 1

Output:
4

Explanation:
Replace the one 'A' in the middle with 'B' and form "AABBBBA".
The substring "BBBB" has the longest repeating letters, which is 4.

 

題解:因爲要求滿足條件的最長子字符串,所以可以採用滑窗法,滑動窗口得到滿足條件的最長子字符串

該題與上面兩題的不同之處是,判斷是否應該滑動左窗口的條件不同,即如何判斷窗口內字符串是否已經不滿足題目要求,一旦不滿足題目要求,就應該滑動左窗口,直至窗口內字符串滿足題目條件

那麼如何判斷窗口內字符串是否已經不滿足條件呢?即通過k次操作已經不能把窗口內全部變爲一種字母,

由於我們可以通過unordered_map來記錄窗口內元素,所以我們可以對map進行統計,我們可以統計將map中字母全變爲一種字母所需要的最少操作,即所有字母個數減去最多的一種字母個數即可

統計出所需要操作的最少次數後,如果該操作次數大於k,說明我們需要移動左窗口了,直至窗口內再次滿足條件爲止,然後繼續移動右窗口

程序如下:

class Solution {
public:
    int characterReplacement(string s, int k) 
    {
        int res = 0;
        int left = 0;
        int right = 0;
        
        unordered_map<int,int> map;
        
        for(right = 0; right < s.size(); right++)
        {
            map[s[right]]++;
            while(count(map) > k)
            {
                res = max(res,right-left);
                map[s[left]]--;
                left++;
            }
        }
        res = max(res,right-left);
        return res;
    }
    
private:
    //統計將窗口內字母全部變爲一種字母所需要的最少操作次數
    int count(unordered_map<int,int>& map)
    {
        int maxdig = 0;
        int sum = 0;
        for(auto it = map.begin(); it != map.end(); it++)
        {
            maxdig = max(maxdig,it->second);
            sum += it->second;
        }
        return sum-maxdig;
        
    }
    
};

當然,遇見問題的時候最重要的是靈活運用!!!

 

 

 

 

 

 

 

 

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