一、题设
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。 - 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC" 输出:"BANC"
示例 2:
输入:s = "a", t = "a" 输出:"a"
示例 3:
输入: s = "a", t = "aa" 输出: "" 解释: t 中两个字符 'a' 均应包含在 s 的子串中, 因此没有符合条件的子字符串,返回空字符串。
二、基本思路
滑动窗口法:如下模板
dic = dict()
window = dict()
for c in t:
dic[c] = dic.get(c,0) + 1
left,right = 0,0 # 窗口左右边界
selected = 0 # 已经收集完成的dic里面的元素
start,lens = 0,99999
while right < len(s):
c = s[right]
进行窗口内数据更新的一系列操作
right += 1
while 右边界已经确定,滑动左边界的条件:
进行窗口内数据更新的一系列操作
接着我们明确以下几个问题:
1、当移动 right 扩大窗口,即加入字符时,应该更新哪些数据?
答:当当前字符在dic中,我们把当前字符c加入window中或者修改其个数,当dic中的c个数与window个数相同时,我们让selected自加,意味着dic中的某一个元素已经收集完成。
2、什么条件下,窗口应该暂停扩大,开始移动 left 缩小窗口?
答:当right收集完成dic中的所有元素以及其个数时,可以暂停扩大。
3、当移动 left 缩小窗口,即移出字符时,应该更新哪些数据?
答:移动left前,应该记录当前的长度和开始下标;移动left时,我们把window中的对应字符数自减,当两字典不同时暂停缩小,同时比较长度替换。
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?
答:缩小窗口。
三、代码实现
class Solution(object):
def minWindow(self, s, t):
dic = dict()
window = dict()
for c in t:
dic[c] = dic.get(c,0) + 1
left,right = 0,0 # 窗口左右边界
selected = 0 # 已经收集完成的dic里面的元素
start,lens = 0,99999
while right < len(s):
c = s[right]
if c in dic:
window[c] = window.get(c,0) + 1
if dic[c] == window[c]:
selected += 1
right += 1
while selected == len(dic):# 右边界已经确定,第第一次收集完成元素
curlen = right - left # 左边界
if curlen < lens:
start = left # 记录起点
lens = curlen # 记录长度
d = s[left]
if d in window:
if window[d] == dic[d]:
selected -= 1
window[d] = window.get(d) - 1
left += 1 # 移动左边界
return s[start:start+lens] if lens!= 99999 else ""
四、效率总结