leetcode76. 最小覆盖子串
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串
""
。 - 如果 S 中存在这样的子串,我们保证它是唯一的答案。
方法:滑窗
思路:
这道题要使用滑窗法,滑窗法主要就是左右两个指针,不断地改变窗口。
本题需要先固定左指针,滑动右指针,使得窗口覆盖的字符串符合条件,记下此时的答案,然后滑动左指针,在符合条件的前提下,进行可能找到最短的符合条件窗口,更新答案。直到不符合条件了,再滑动右指针,直到符合条件,再滑动左指针缩小答案窗口......重复过程直到右指针抵达右边界,此时ans存储的最短的窗口就是答案。
这里,我们通过两个哈希表,存储T子串的元素情况,和S窗口中,与T相关的元素的情况,编写一个子函数来判断是否符合条件。
代码:
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t or len(s)<len(t): #排除特殊情况
return ''
t_need = {x:0 for x in t}
temp = t_need.copy() #初始化窗口的字符情况temp
for x in t:
t_need[x] += 1 #t的字符情况{字符:次数}
left = right = 0 #初始化左右指针
n = len(s)
ans = [0,n] #初始化ans,ans表示答案窗口的左右位置,初始化为最大
while right < n: #右指针扩大窗口
if s[right] in temp: #扩大的这个字符是t中的,更新temp
temp[s[right]] += 1
while self.compare(temp,t_need):
#如果满足条件了,开始left缩小窗口
if right-left < ans[1]-ans[0]: #窗口更小的话,更新答案
ans = [left,right]
if s[left] in temp: #缩小的字符在temp中,更新temp
temp[s[left]] -= 1
left += 1
right += 1
return s[ans[0]:ans[1]+1] if ans != [0,n] else '' #最后返回答案
def compare(self,x,y): #判断是否符合条件,每个元素temp中的次数都要不小于t_need
for s in x:
if x[s] < y[s]:
return False
return True
结果:
![57dc56c25a3a05cffd9d3e5a697c457b.png](https://img-blog.csdnimg.cn/img_convert/57dc56c25a3a05cffd9d3e5a697c457b.png)
优化:
思路:
上面方法是否有地方可以优化呢?
答案是肯定的,上面的方法在每次判断是否符合条件的时候,都要调用子函数,对两个哈希表进行遍历比较,每次比较时间复杂度达到O(N),我们需要优化到一个常量O(1)。
我们设定一个变量distance,distance_t表示t需要的字符总数,distance表示当前窗口中t需要的字符数量,如果distance >= distance_t,那么就符合条件了,这样就优化到一个常量比较了。但是有一个问题就是,单纯的数量多不能代表符合情况,比如窗口中只有一个t中字符,但是有100个,可能也使得距离更大,但是不符合条件。
因此,对于distance,我们这么设置,在移动右指针的时候,在这个字符不满足条件情况下,对distance+1,对于已经满足条件的字符,pass,这样只要distance == distance_t,那么就是符合条件的。在左指针缩小窗口时,如果查询这个字符在s中的次数大于t需要这个字符的次数,那么还是满足条件的,distance不用处理,如果查询到这个字符在s中的次数等于t需要这个字符的次数,那么这个字符去掉之后,就不满足条件了,所以将distance-1,退出循环,继续right扩张,这就完成了使用常量来判断状态。
代码:
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t or len(s)<len(t): #排除特殊情况
return ''
t_need = {x:0 for x in t}
temp = t_need.copy() #初始化窗口的字符情况temp
distance = 0
distance_t = 0
for x in t:
t_need[x] += 1 #t的字符情况{字符:次数}
distance_t += 1
left = right = 0 #初始化左右指针
n = len(s)
ans = [0,n] #初始化ans,ans表示答案窗口的左右位置,初始化为最大
while right < n: #右指针扩大窗口
if s[right] in temp: #扩大的这个字符是t中的,更新temp
if temp[s[right]] < t_need[s[right]]:
distance += 1
temp[s[right]] += 1
while distance == distance_t:
#如果满足条件了,开始left缩小窗口
if right-left < ans[1]-ans[0]: #窗口更小的话,更新答案
ans = [left,right]
if s[left] in temp: #缩小的字符在temp中,更新temp
if temp[s[left]] == t_need[s[left]]:
distance -= 1
temp[s[left]] -= 1
left += 1
right += 1
return s[ans[0]:ans[1]+1] if ans != [0,n] else '' #最后返回答案
结果:
![3414ad77842b1ecee9231772f631651a.png](https://img-blog.csdnimg.cn/img_convert/3414ad77842b1ecee9231772f631651a.png)