滑動視窗總結

2021-10-20 17:30:27 字數 4248 閱讀 4279

乙個序列中,滿足某些條件的子串的最長/最短/個數

時間複雜度分析:暴力解法的時間複雜度一般為o(n ^ 3):o(n ^ 2)列舉所有的子串,o(n)判斷是否滿足條件。

滑動視窗在兩個方面都降低時間複雜度:首先並不列舉所有的子串,只列舉可能包含答案的那些。其次通過記錄視窗內子串的一些資訊,使得判斷是否滿足條件的時間複雜度下降。

難點:什麼時候更新答案,視窗內儲存哪些資訊來降低判斷的時間。

正確性證明:分解為子問題,或者反證法證明視窗內一定包含過正確答案。

這一類題目的特點是:一開始子串就滿足條件,然後不斷擴大視窗直至不滿足條件,再縮小視窗直至滿足條件,每次擴大視窗並判斷滿足條件後都更新答案。

3. 無重複字元的最長子串:給定乙個字串,請你找出其中不含有重複字元的最長子串的長度。

解法:視窗的範圍為[left,right),用乙個字典記錄視窗中的字元及其出現次數,當視窗內的子串滿足條件時擴大視窗(right右移)並更新答案不滿足條件時縮小視窗(left右移)。

正確性證明:實際上我們把問題分解為了多個子問題:求以s[i]起點的滿足條件的子串中最長的,再將子問題答案合併即可。

但還有乙個問題:left每次並不是只向前走一步,如何保證列舉每乙個s[i]呢?假設[left,right)滿足條件,[left,right+1)不滿足條件,此時答案至少是right-leftleft向前走k步直至滿足條件的過程中,以s[left]為起點的滿足條件的子串,長度都不可能超過right-left,也就是說這些子問題我們雖然沒有求,但它們的答案沒有意義,本身也就不需要求。

時間複雜度分析:leftright均沒有後退:o(n)。判斷right-left==len(window):o(1)。故總時間複雜度為o(n)。

**:

class

solution

:def

lengthoflongestsubstring

(self, s:

str)

->

int:

ls =

len(s)

window =

dict()

left = right =

0 ans =

0while rightc = s[right]

right +=

1 window[c]

= window.get(c,0)

+1while

len(window)

d = s[left]

left+=

1 window[d]-=1

if window[d]==0

: window.pop(d)

ans =

max(ans,right-left)

return ans

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

76. 最小覆蓋子串:給你乙個字串s、乙個字串t。返回s中涵蓋t所有字元的最小子串。如果s中不存在涵蓋t所有字元的子串,則返回空字串""

解法:視窗的範圍為[left,right),用乙個字典記錄視窗中的字元及其出現次數,當視窗內的子串不滿足條件時擴大視窗(right右移),滿足條件時縮小視窗(left右移)並更新答案

正確性證明:類似上一題,把問題分解為子問題:求以s[i]終點的滿足條件的子串中最短的。

假設[left,right)是第乙個滿足條件的區間,則所有終點小於right的子問題都不必再考慮。之後依次列舉每乙個right。但每次列舉時,left並沒有從最左邊開始找,是否可能漏掉一些答案呢?其實和上一題類似,如果[left,right)是當前子問題的最優解,則下乙個子問題從[left+1,right+1)開始找,即使在left+1左邊存在該子問題的解,對於最終的最優解也是沒有意義的,故不需要考慮。

時間複雜度分析:leftright均沒有後退:o(n)。判斷是否滿足條件:o(1)。故總時間複雜度為o(n)。

**:

class

solution

:def

minwindow

(self, s:

str, t:

str)

->

str:

ls,lt =

len(s)

,len

(t) need =

# t中的元素

for i in t:

need[i]

= need.get(i,0)

+1window =

# 視窗中的元素

left = right =

0# 視窗範圍

start = length =

float

('inf'

)# 答案

valid =

0# window是否包含t

while right# 更新視窗內資訊

c = s[right]

window[c]

= window.get(c,0)

+1if c in need and window[c]

== need[c]

: valid+=

1 right +=

1# 更新答案

while valid ==

len(need)

:if right - left < length:

start, length = left, right-left

c = s[left]

window[c]-=1

if c in need and window[c]

< need[c]

: valid -=

1 left +=

1return s[start:start+length]

if length<

float

('inf'

)else

''

滑動視窗為什麼快?實際上是因為它把原問題分解為了子問題,而其中很大一部分子問題是不需要求解的(最優解一定不出現在這些子問題之中)。故要求問題一定要有二段性質:

求最長時,對於任意子問題s[i],一定存在j>i,使得s[i...k],i總而言之,求解最長問題的子問題s[left]的過程中,right向右移時要先滿足再不滿足;求解最短問題的子問題s[right]的過程中,left右移時也要先滿足再不滿足。這樣才可以用滑動視窗演算法。

395. 至少有k個重複字元的最長子串,由於不滿足上述條件,就無法直接使用滑動視窗演算法。

滑動視窗總結

滑動視窗思想 視窗由兩個指標構成,乙個左指標left,乙個右指標right,然後 left,right 表示的索引範圍是乙個視窗了。右指標right的功能是用來擴充套件視窗 當視窗內的條件沒有達到題目要求時,我們需要不斷移動右指標right直到視窗內的條件第一次滿足題目要求為止。左指標left的功能...

滑動視窗總結

本部落格基於大佬的題解 滑動視窗是一種高階的雙指標演算法,一般用於字串匹配問題 最小覆蓋子串 找到字串中所有字母異位詞 無重複字元的最長子串 字串的排列 滑動窗的基本思路就是維護乙個視窗,不斷滑動。該演算法的大致邏輯如下 int left 0,right 0 while right s.size 演...

滑動視窗總結

1.我們使用雙指標的思路,初始定義兩個指標,left right 0,把索引閉區間 left,right 看作乙個滑動視窗 2.然後不斷增加right的值,直到視窗中的字串符合要求 3.然後,停止增加right的值,轉而增加left的值,直到視窗中的字串不符合要求,每次增加left,就要更新一輪結果...