美团--最长全1串
美團–最長全1串
文章目錄
- 美團--最長全1串
- 一、問題描述
- 二、分析
- 三、代碼
一、問題描述
給你一個01字符串,定義答案=該串中最長的連續1的長度,現在你有至多K次機會,每次機會可以將串中的某個0改成1,現在問最大的可能答案
- 輸入描述:
- 輸出描述:
二、分析
這道題和很多題類似:
- 力扣424. 替換后的最長重復字符:替換后的最長重復字符
- 字節-字符串翻轉:字符串翻轉
這幾道題基本上是一樣的,這道題也是:力扣1004. 最大連續1的個數 III:最大連續1的個數
- 首先考慮本問題的最直觀樸素的想法:枚舉出每一個子串,逐個驗證其有效性并且更新最大值。
- 這種做法的時間復雜度是O(n^3),在這一數據規模下是一定會超時的,超時的原因是做了非常多重復的驗證計算,且枚舉了非常多沒有意義的串
- 那么基于我們在這類連續子數組性質問題的經驗,使用雙指針(滑動窗口)會是一個非常好的想法
- 雙指針解法
- 雙指針解法的重點在于維護兩個指針:
- 設置兩個指針(表現為數組下標值),左指針指向當前子串左邊界,右指針指向當前子串右邊界,我們通過某種方法使得左右指針之間的當前子串符合要求:即 '0' 的數目小于等于 K
- 又:我們很容易知道,最長串一定出現在串內 '0' 的數目飽和(如果'0'比較多)或者最大(如果 K 比較大)的情況下,因此我們需要盡可能地把右指針右移
- 那么固定左指針時,什么情況下可以使右指針右移呢?轉化次數K有剩余 或右指針的下一位是'1'
- 如此移動右指針,即可找到每一個左指針所對應的最長有效串,而且右指針指向的元素的下一位一定是’0’
- 那么我們可以把每一個數組元素作為左指針,以上述方式枚舉出最長有效串,然后返回結果
- 但是還是不夠快
- 不夠快的原因還是做了重復計算,在左指針變化時,右指針從左指針處開始右移,沒有有效利用上一次操作的結果,造成了大量沒有必要的操作;那么如何避免重復計算呢?
- 我們的關鍵在于剛剛提到的“0數飽和”。即:可能成為最長串的串,它內部'0'的數量一定是飽和或者最大的
- 那么我們可以通過某種操作來使得在左指針右移之后,我們立即就能獲得新左指針對應的'0'數飽和串/'0'數最大串
- 首先我們考察左指針右移一位時的情況:
- 首先我們需要清楚一個事實:當右指針到達串尾部時,左指針沒有任何必要繼續右移,因為左指針右移的話子串的長度一定會減小,并且飽和性質無法保證
- 也就是說,左指針右移的條件是:右指針沒有觸碰串的右邊界
- 那么我們會得到一個推論:當左指針需要右移時,串中的'0'一定是飽和的而不是最大的(“飽和”指子串中0的數目與K相等,不可以再轉化0為1;“最大”指整個串中所有的0均已被轉化為1)既飽和又最大的情況也不需要右移
- 得到這個推論,我們就可以來考慮左指針右移時的情況:
- 當前左指針指向的元素是'0'時:當左指針右移時子串內的'0'數減一,為了維護其飽和性質,我們剛剛提到“右指針指向的元素的下一位一定是'0'”,所以右指針一定可以右移至少一位(如果右移之后的下一位是'1'則可以繼續右移)
- 當前左指針指向的元素是'1'時:當左指針右移時子串內的'0'數不變,由于我們維護了每一個子串的飽和性質,右指針不可以右移
- 每一次左指針移動及之后右指針移動的一系列操作結束之后,更新最大值。通過以上的操作,遍歷直到右指針觸碰數組右邊界,即可得到全局的最大符合條件串的長度
- 開銷分析
- 平均漸進時間復雜度O(n)
- 漸近額外空間O(1)
三、代碼
class Solution { public:int longestOnes(vector<int>& A, int K) {//在這個實現中,right指的是當前子串末尾的下一位int left(0), right(0);int max(0);while (left <= right && right < A.size()) {//如果right沒有走到結尾,并且right位置為1(可以直接++)或者//right位置為0(如果k不等于0)那么可以進行一次0-》1轉變,//繼續++)while (right < A.size() && (A[right] || K != 0)) {//如果是0,那么k的值需要--if (!A[right]) K--;right++;}//更新結果max = std::max(right - left, max);//如果left位置為1,只需要left++if (A[left]) left++;//反之代表left位置為0,那么我們需要同時++else {left++, right++;}}return max;} };總結
- 上一篇: 美团--美团骑手包裹区间分组
- 下一篇: 力扣--替换后的最长重复字符