1. 程式人生 > 其它 >leetcode 滑動視窗小結 (二)

leetcode 滑動視窗小結 (二)

技術標籤:# 演算法:滑動視窗leetcode刷題筆記字串滑動視窗

目錄

424. 替換後的最長重複字元

https://leetcode-cn.com/problems/longest-repeating-character-replacement/
給你一個僅由大寫英文字母組成的字串,你可以將任意位置上的字元替換成另外的字元,總共可最多替換 k 次。在執行上述操作後,找到包含重複字母的最長子串的長度。

注意:
字串長度 和 k 不會超過 10^4。

示例 1:

輸入:
s = “ABAB”, k = 2
輸出:
4
解釋:
用兩個’A’替換為兩個’B’,反之亦然。

示例 2:

輸入:
s = “AABABBA”, k = 1
輸出:
4
解釋:
將中間的一個’A’替換為’B’,字串變為 “AABBBBA”。
子串 “BBBB” 有最長重複字母, 答案為 4。

思考分析1

需要注意的地方:

1、何時擴充視窗?
當子串符合要求,向右擴充一位。(貪心思想,滿足了要求還要繼續膨脹)
2、子串達成什麼條件能說明符合要求?
如果最大頻數 + k >= 當前視窗長度,(也就是說經過k此修改,我們可以將不是出現次數最多的元素修改為頻數最高元素,從而變為重複串,並且這個串的長度是大於此時視窗長度的)那麼我們認為是符合條件的,此時更新視窗長度,取歷史視窗長度與當前視窗長度的較大值

3、什麼時候滑動視窗
當子串不滿足條件的時候,視窗整體向右滑動一位,視窗長度不會減少。

class Solution {
public:
    int characterReplacement(string s, int k) {
        int left = 0, right = 0;
        //當前視窗中元素的最高頻數
        int now_max_freq = 0;
        int hash_map[26] ={0};
        //視窗長度最大值
        int max_window_length = 0;
        while(right <
s.size()) { //新加入視窗的元素 char c = s[right]; //視窗內這個元素對應的頻數+1 hash_map[c - 'A']++; //找到整個視窗內最大頻數 for(int i = 0; i < 26; i++) now_max_freq = max(now_max_freq,hash_map[i]); //如果最大頻數 + k >= 當前視窗長度,那麼我們認為是符合條件的,此時更新視窗長度,取歷史視窗長度與當前視窗長度的較大值 if(now_max_freq + k >= right - left + 1) { max_window_length = max(max_window_length,right - left + 1); } //如果不滿足整個條件,我們需要將整個視窗平移 else { char d = s[left]; hash_map[d - 'A']--; left++; } right++; } return max_window_length; } };

優化

關於優化,首先得知道一點就是我們之前更新視窗長度的條件:
1、先找這個視窗內的最大頻數
2、如果這個頻數 + k >= 當前視窗長度,我們選擇更新視窗長度
由於在尋找最大頻數的時候有個比較過程,並且每次新進來一個字元我們就得重新比較26次。(我們可以優化比較,例如加入備忘錄什麼的)。但這裡我們不需要這樣做。
我們只需要找到“歷史視窗內元素出現最大頻數”,然後觀察這個頻數 + k 是否 >= 當前視窗長度。
由於這道題目要求求解的是最長重複子串,如果當前視窗最大字元重複個數小於歷史視窗的最大字元重複個數,完全可以將此視窗忽略掉,因為它必然不可能是最長重複子串。只有當歷史視窗的最大字元重複個數更新時,其最大長度才會進行相應的更新。
現在我們將上面的程式碼稍作修改,改成如下程式碼:

class Solution {
public:
    int characterReplacement(string s, int k) {
        int left = 0, right = 0;
        //歷史最大頻數
        int history_max_freq = 0;
        int hash_map[26] ={0};
        int max_window_length = 0;
        while(right < s.size())
        {
            //新加入視窗的元素
            char c = s[right];
            hash_map[c - 'A']++;
            //觀察整個元素是不是視窗內出現次數最多的元素,如果是更新history值
            history_max_freq = max(history_max_freq,hash_map[c - 'A']);
            if(history_max_freq + k >= right - left + 1)
            {
                max_window_length = max(max_window_length,right - left + 1);
            }
            else
            {
                char d = s[left];
                //視窗左移,被移除的元素在視窗內頻數-1
                hash_map[d - 'A']--;
                left++;
            }
            right++;
        }
        return max_window_length;
    }
};

兩種解法效能相差挺大的。
在這裡插入圖片描述
下面一題和本題幾乎一模一樣,甚至是簡化,我們也同樣用兩種思路來做吧。

1004. 最大連續1的個數 III

https://leetcode-cn.com/problems/max-consecutive-ones-iii/
給定一個由若干 0 和 1 組成的陣列 A,我們最多可以將 K 個值從 0 變成 1 。

返回僅包含 1 的最長(連續)子陣列的長度。
示例 1:

輸入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
輸出:6
解釋:
[1,1,1,0,0,1,1,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子陣列長度為 6。

示例2:

輸入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
輸出:10
解釋:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗體數字從 0 翻轉到 1,最長的子陣列長度為 10。

友情提醒

由於這一題的簡化性,最好好好看看兩者程式碼有何區別,不然以為是一樣的。(其實效能應該差不多,因為這裡在找當前最大頻數的時候不需要比較26次了,只需要一次。)

方法1,基於當前最大頻數

class Solution {
public:
    int longestOnes(vector<int>& A, int K) {
        int left = 0, right = 0;
        int one_times = 0;
        int now_max_freq = 0;
        int max_window_length = 0;
        while(right < A.size())
        {
            int c = A[right];
            if(c == 1) one_times++;
            if(one_times + K >= right - left + 1)
            {
                max_window_length = max(max_window_length,right - left + 1);
            }
            else
            {
                int d = A[left];
                if(d == 1) one_times--;
                left++;
            }
            right++;
        }
        return max_window_length;
    }
};

方法2,基於歷史最大頻數

class Solution {
public:
    int longestOnes(vector<int>& A, int K) {
        int left = 0, right = 0;
        int one_times = 0;
        int history_max_freq = 0;
        int max_window_length = 0;
        while(right < A.size())
        {
            int c = A[right];
            if(c == 1) one_times++;
            history_max_freq = max(history_max_freq,one_times);
            if(history_max_freq + K >= right - left + 1)
            {
                max_window_length = max(max_window_length,right - left + 1);
            }
            else
            {
                int d = A[left];
                if(d == 1) one_times--;
                left++;
            }
            right++;
        }
        return max_window_length;
    }
};