1、题目描述
https://leetcode-cn.com/problems/minimum-window-substring/
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
输入:S = "ADOBECODEBANC", T = "ABC"
输出:"BANC"
- 如果 S 中不存这样的子串,则返回空字符串 ""。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
2、代码详解
左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动
from collections import Counter
from collections import defaultdict
class Solution:
def minWindow(self, s, t):
# 最短子串开始位置和长度
start = 0
min_len = float('Inf')
left, right = 0, 0
# 两个计数器
needs = Counter(t)
# print(needs) # Counter({'A': 1, 'C': 1, 'B': 1})
# print(len(needs)) # 3
window = defaultdict(int)
# defaultdict(int)在访问的key不存在的时候返回默认值0, 可以减少一次逻辑判断
# list对应[ ],str对应的是空字符串,set对应set( ),int对应0
match = 0 # 表示窗口中满足need条件的字符个数
while right < len(s):
c1 = s[right] # 将移入窗口的字符
if needs[c1] > 0:
window[c1] += 1 # 如果一个字符进入窗口,应该增加window计数器
if window[c1] == needs[c1]: # 发现某个字符在window的数量满足了need的需要,就要更新match
match += 1 # 表示有一个字符已经满足要求
right += 1 # 右移窗口
# 当match == len(needs)时,说明T中所有字符已经被覆盖,已经得到一个可行的覆盖子串,
# 现在应该开始"收缩窗口"了,以便得到「最小覆盖子串」
# 判断左侧窗口是否要收缩
while match == len(needs):
if right - left < min_len:
# 更新最小子串长度
min_len = right - left
start = left
c2 = s[left] # 将移出窗口的字符
if needs[c2] > 0:
window[c2] -= 1 # 如果一个字符将移出窗口的时候,应该减少window计数器
if window[c2] < needs[c2]:
match -= 1
left += 1 # 左移窗口
return s[start:start + min_len] if min_len != float("Inf") else ""
s = "EBBANCF"
t = "ABC"
S = Solution()
print(S.minWindow(s, t)) # 'BANC'