推薦閱讀:算法思維繫列/滑動窗口技巧.
子串問題
LC76. Minimum Window Substring
from collections import defaultdict
class Solution:
def minWindow(self, s: str, t: str):
left, right = 0, 0
window = defaultdict(int)
# “給出2字符串S和T,求S中包含T的所有字母的某些子串”需要的變量
needs = defaultdict(int)
match = 0 # window匹配了多少種needs中的字符
for c in t: # 初始化needs
needs[c] += 1
# 本題需要的記錄返回值的變量
start, min_len = 0, float('inf')
while right < len(s):
c1 = s[right] # 右移right的同時需要維護window和match(5行)
if c1 in needs:
window[c1] += 1
if window[c1] == needs[c1]: # 注意這裏是=而不是≥,因爲只需要在相等時記錄該字符的次數已經足夠,如果後面還有該字符,也不能加大match,所以對某個字符來說只會加一次match
match += 1
while match == len(needs): # 每右移一步就檢查是否滿足題意的充分條件(match==len(needs)說明所有字母都已出現,但是字母出現次數大於等於需要的次數),符合則將left右移多步直到不滿足
if right - left + 1 < min_len: # 檢查是否滿足題意的必要條件(找到符合條件的最短子串)
start = left
min_len = right - left + 1
c2 = s[left] # 右移left的同時需要維護window和match(5行)
if c2 in needs:
window[c2] -= 1
if window[c2] < needs[c2]: # 注意這裏是<,當≥時都只減去window[c2],不見match,直到減完後小於needs[c2],這時減match,而下輪循環就會跳出(match != len(needs))。所以整個循環只會減一次match
match -= 1
left += 1 # 內層循環的末尾將left右移
right += 1 # 外層循環的末尾將right右移
return s[start:start + min_len] if min_len < float('inf') else ''
LC438. Find All Anagrams in a String
題目鏈接:Find All Anagrams in a String
from collections import defaultdict
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
left,right=0,0
window=defaultdict(int)
# “給出2字符串S和T,求S中包含T的所有字母的某些子串”需要的變量
needs=defaultdict(int)
match=0
for c in p: # 初始化needs
needs[c]+=1
# 本題需要的記錄返回值的變量
res=[]
while right<len(s):
c1=s[right] # 右移right的同時需要維護window和match(6行,前5行是爲了right+=1)
if c1 in needs:
window[c1]+=1
if window[c1]==needs[c1]:
match+=1
right+=1
while match==len(needs): # 每右移一步就檢查是否滿足題意的充分條件(match==len(needs)說明所有字母都已出現,但是字母出現次數大於等於需要的次數),符合則將left右移多步直到不滿足
if right-left+1==len(p): # 檢查是否滿足題意的必要條件(子串長度恰好等於模板長度),是則更新返回值
res.append(left)
c2=s[left] # 右移left的同時需要維護window和match(6行,前5行是爲了left+=1)
if c2 in needs:
window[c2]-=1
if window[c2]<needs[c2]:
match-=1
left+=1
return res
LC3. Longest Substring Without Repeating Characters
題目鏈接:Longest Substring Without Repeating Characters
PS:注意本題的window存放字符次數的含義與上面兩題不同,前面兩題window存放的是窗口left到right(包含)子串中出現了needs中的字符以及次數,而本題沒有needs,window記錄的是窗口中所有字符的次數
from collections import defaultdict
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
left,right=0,0
window=defaultdict(int)
# 本題需要的記錄返回值的變量
res=0
while right<len(s):
c1=s[right] # 右移right的同時需要維護window(3行,前2行是爲了right+=1)
window[c1]+=1
right+=1
while window[c1]>1: # 每右移一步就檢查是否滿足題意的“充分”條件(子串含有重複字符),符合則將left右移多步直到不滿足
c2=s[left] # 右移left的同時需要維護window(3行,前2行是爲了left+=1)
window[c2]-=1
left+=1
# 只有結束了內層循環才滿足必要條件(子串無重複字符),此時檢查是否需要更新返回值
res=max(res,right-left+1)
return res
劍指offer:和爲S的連續正數序列
題目鏈接:https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe
參考答案:
# -*- coding:utf-8 -*-
class Solution:
def FindContinuousSequence(self, tsum):
# https: // www.nowcoder.com / questionTerminal / c451a3fd84b64cb19485dad758a55ebe?f = discussion
result = []
# 兩個起點,相當於動態窗口的兩邊,根據其窗口內的值的和來確定窗口的位置和大小
low, high = 1, 2
while low < high:
# 由於是連續的,差爲1的一個序列,那麼求和公式是(a0+an) * n / 2
cur = (high + low) * (high - low + 1) / 2
# 相等,那麼就將窗口範圍的所有數添加進結果集
if cur == tsum:
result.append([i for i in range(low, high + 1)])
low += 1
# 如果當前窗口內的值之和小於sum,那麼右邊窗口右移一下
elif cur < tsum:
high += 1
else:
# 如果當前窗口內的值之和大於sum,那麼左邊窗口右移一下
low += 1
return result
其他
劍指offer:遞增數組中和爲S的兩個數字
題目鏈接:https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b
解題思路:
參考答案:
# -*- coding:utf-8 -*-
class Solution:
def FindNumbersWithSum(self, arr, tsum):
# 鏈接:https://www.nowcoder.com/questionTerminal/390da4f7a00f44bea7c2f3d19491311b?f=discussion
n = len(arr)
i, j = 0, n - 1
while i < j:
cur = arr[i] + arr[j]
if cur == tsum:
return [arr[i],arr[j]]
elif cur > tsum:
j-=1
else:
i+=1
return []