题目
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串 “”。
- 如果 S 中存在这样的子串,我们保证它是唯一的答案。
解题思路
定义 left, right 分别指向滑动窗口的左右边界,子字符串为 s[left, right) 左闭右开区间。使用 right 指针向右搜索,同时要记录在 s[left, right) 这个区间覆盖了多少个 t 中的元素。如果在 s[left,right) 内,覆盖了所有 t 的元素,说明这个区间是符合要求的一个区间。此时 right 指针再向右移动已经没有意义。现在要移动 left 指针,直至 s[left,right) 子字符串不能覆盖 t 。
在 s[left, right) 区间内的元素出现个数应该把 t 中所有的元素都包含,所以我们定义 t_dic 字典变量保存 t 中的元素出现次数,s_dic 字典变量保存 s[left, right) 滑动窗口中的相应字符出现的次数。 count 变量储存 s[left, right) 闭区间中已经覆盖了 t 中的多少个元素,如果 cnt == len(t_dic) 说明该子字符串覆盖了 t 中所有字符,这时已经得到一个可行的覆盖子串,可以开始收缩窗口了,以便得到最小覆盖子串。
在移动 left 指针收缩窗口的时候要注意存储最短的子串,使用的 minLen 变量存储当前满足题目要求的最短子串长度。当某个子字符串区间满足要求时,根据minLen更新最终的最小覆盖子串。
代码
class Solution:
def minWindow(self, s: str, t: str) -> str:
need=collections.defaultdict(int)
m = len(s)
n = len(t)
# 滑动窗口中相应出现的字符及其对应的个数
s_dic = {}
# t中出现的字符及其对应的个数
t_dic = {}
# 滑动窗口左右位置,左闭右开区间 [left, right)
left = 0
right = 0
# 滑动窗口中满足t_dic中条件的字符个数
count = 0
# 记录最小覆盖子串的起始索引和长度
start = 0
minlen = float('inf')
# 将t中的字符加入t_dic字典
for i in t:
if i in t_dic:
t_dic[i] += 1
else:
t_dic[i] = 1
while right<m:
# c是将移入窗口的字符
c = s[right]
# 右移窗口
right += 1
# 滑动窗口内数据更新
if c in t_dic:
if c in s_dic:
s_dic[c] += 1
else:
s_dic[c] = 1
if s_dic[c] == t_dic[c]:
count += 1
# 判断左侧窗口是否要收缩
while count==len(t_dic):
# 更新最小覆盖子串的起始索引和长度
if right-left<minlen:
start = left
minlen = right-left
# d是将移出窗口的字符
d = s[left]
# 左移窗口
left += 1
# 滑动窗口内数据更新
if d in t_dic:
if s_dic[d] == t_dic[d]:
count -= 1
s_dic[d] -= 1
return '' if minlen == float('inf') else s[start:start+minlen]