題目列表
- Leetcode 3. 無重複字符的最長子串
- Leetcode 30. 串聯所有單詞的子串
- Leetcode 76. 最小覆蓋字串
- leetcode 209. 長度最小的子數組
- leetcode 239. 滑動窗口最大值
- Leetcode 438. 找到字符串中所有字母異位詞
- Leetcode 567. 字符串的排列
- Leetcode 632. 最小區間
題目分析
滑動窗口,維護一個連續區間 ,右邊界不斷擴展,滿足一定條件時更新左邊界,直至遍歷整個數據。
滑動窗口的題目要明確何時更新右邊界,何時更新左邊界,可解決大部分求連續子串的問題。
Leetcode 3. 無重複字符的最長子串
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。
輸入: “abcabcbb”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “abc”,所以其長度爲 3。
維護一個滑動窗口:
何時更新右邊界? 窗口內的字符不重複。
何時更新左邊界? 新進入的字符與窗口內的字符存在重複。
對於例題中的 abcabcbb,滑動窗口(窗口)左右邊界的座標初始爲0,然後右邊界不斷擴展,直到窗口內爲 abc 時滿足題目要求,右邊界繼續擴展,此時再進入 a,窗口變成了 abca,這時候不滿足要求。因此需要移動這個隊列的左邊界。左邊界只需要不斷彈出元素直到滿足窗口內字符不重複,比如壓入 a 時,窗口內 abc 以及包含了 a,只需要把窗口內 a 及之前的字符彈出即可。
優化:每次壓入都要遍歷一遍窗口判斷是否有相同字符,這樣最壞情況下的複雜度爲 。爲了進一優化目標,可以使用一個map,記錄在窗口的每個字符的位置,這樣在新進入字符時只需要訪問 map,即可判斷窗口內是否有該字符,並且有的話可以直接獲得該字符在窗口內的位置。每次進入字符時,更新窗口的最大大小,即可得到最長字串的長度。整個算法的時間複雜度變爲 。
Leetcode 76. 最小覆蓋字串
給你一個字符串 S、一個字符串 T,請在字符串 S 裏面找出:包含 T 所有字母的最小子串。
輸入: S = “ADOBECODEBANC”, T = “ABC”
輸出: “BANC”
仍然是滑動窗口:
何時更新右邊界? 窗口內的字符沒有包含所有的T中的字符。
何時更新左邊界? 窗口內已經包含了所有T中的字符。
滑動窗口首先不斷擴展右邊界,直到尋找到一個可行的子串包含T中的所有字母,然後更新左邊界,不斷收縮窗口來尋得滿足條件的最短窗口大小。當更新左邊界後窗口無法包含所有T中的字母時,再更新右邊界,依次循環求解。
優化: 使用一個map記錄T中每個字符需要的個數,然後更新右邊界時,新進入的字符s如果是T中的字符,那麼map[s]–;更新左邊界時,新彈出的字符s如果是T中的字符,那麼map[s]++;如果map中所有的value之和小於等於0,說明窗口內已經滿足包含T中的所有字符,否則沒有包含。
Leetcode 438. 找到字符串中所有字母異位詞
給定一個字符串 s 和一個非空字符串 p,找到 s 中所有是 p 的字母異位詞的子串,返回這些子串的起始索引。
字符串只包含小寫英文字母,並且字符串 s 和 p 的長度都不超過 20100。
說明:
- 字母異位詞指字母相同,但排列不同的字符串。
- 不考慮答案輸出的順序。
輸入:
s: “cbaebabacd” p: “abc”
輸出:
[0, 6]
解釋:
起始索引等於 0 的子串是 “cba”, 它是 “abc” 的字母異位詞。
起始索引等於 6 的子串是 “bac”, 它是 “abc” 的字母異位詞。
固定長度的滑動窗口。
何時更新右邊界? 窗口小於p的長度。
何時更新左邊界? 新進入的字符不在p中或者窗口等於p的長度。
Note:使用一個map記錄p中所需字符及其個數,再使用一個map記錄窗口內的字符及其個數,兩者相等時即滿足條件。
題目代碼
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
start = 0
maxcount = 0
mapV = {}
# 右邊界擴展
for i in range(len(s)):
# 是否更新左邊界
start = max(start, mapV.get(s[i], -1))
# 更新新進入的字符的座標
mapV[s[i]] = i + 1
# 更新窗口最大的大小
maxcount = max(maxcount, i - start + 1)
return maxcount
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
if len(words)==0:
return []
record = {}
for word in words:
record[word] = record.get(word, 0) + 1
winRecord = {}
start = end = 0
ans = []
length = len(words[0])
while(end<len(s)-length+1):
word = s[end:end+length]
if word in record.keys():
end += length
winRecord[word] = winRecord.get(word, 0) + 1
if end - start == len(words)*length:
if winRecord==record:
ans.append(start)
start = end = start + 1
winRecord.clear()
else:
winRecord.clear()
start = end = start + 1
return ans
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
tCount = dict()
for w in t:
tCount[w] = tCount.get(w, 0) + 1
start = 0
minStr = s + "0"
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
for i in range(len(s)):
if s[i] in tCount.keys():
tCount[s[i]]-=1
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
while not NotAllIn:
if i-start+1 < len(minStr):
minStr = s[start:i+1]
if s[start] in tCount.keys():
tCount[s[start]] += 1
NotAllIn = any(map(lambda x: x > 0, tCount.values()))
start = start + 1
return "" if len(minStr) > len(s) else minStr
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
words = {}
for w in p:
words[w] = words.get(w, 0) + 1
winwords = {}
ans = []
start = -1
for i in range(len(s)):
if s[i] in words.keys():
if start == -1:
start = i
winwords[s[i]] = winwords.get(s[i], 0) + 1
if i-start+1==len(p):
if winwords == words:
ans.append(start)
winwords[s[start]] -= 1
start += 1
else:
start = -1
winwords.clear()
return ans