推荐阅读:算法思维系列/滑动窗口技巧.
子串问题
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 []