Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = "ADOBECODEBANC", T = "ABC" Output: "BANC"
Note:
- If there is no such window in S that covers all characters in T, return the empty string
""
. - If there is such window, you are guaranteed that there will always be only one unique minimum window in S.
思路
这是道hard题,终于勉强做了出来。题目问:如果S中有substring包含T中所有字符,最短的可能性是什么?
有个坑就是T中可能会有重复的字符。例如aa。那么答案字符串也必须包含两个a。三个月前做的时候就被坑了,三个月后又被坑了。
做法就是滑动窗口。left和right来记录这个窗口的两端。这个窗口里的东西就是答案。我们需要找到这个窗口最短的时候。
试想,我们需要不断验证这个窗口里的字符串是否符合要求,可以使用一个dict来记录每个字母还需要的个数,如果这些个数都小于等于零了,那这个窗口里的字符串就符合要求了。
起初,left和right都在0。
首先,我们的right要不断地往右走,直到这个窗口符合要求。这时候左边有可能会有冗余。
DOADOBECODEBANC
↑
例如上面这样,左边的DO就是多余的。
然后我们left开始往右走,走到这个窗口恰好符合就要不符合要求时停止。按上面那个例子就是说走到A,如果再移动,把A去掉,就不符合要求了。
这时候就是一个解了,更新答案。
然后重复这个过程:将right移向下一个T中存在的字母,将left移动到去掉冗余。
代码
思路就很难想了,代码更是难写。
我的版本:
from collections import Counter
class Solution:
def minWindow(self, s: str, t: str) -> str:
left = 0
counts = Counter(t)
remain = counts.copy()
ans = None
for right in range(len(s)):
curr = s[right]
if len(remain) != 0:
if curr in remain.keys():
remain[curr] -= 1
if remain[curr] <= 0:
remain.pop(curr)
if curr in counts.keys():
counts[curr] -= 1
if len(remain) == 0:
while left < right:
while s[left] not in counts.keys():
left += 1
counts[s[left]] += 1
if counts[s[left]] > 0:
counts[s[left]] -= 1
break
else:
left += 1
if not ans or len(ans) > right - left:
ans = s[left:right+1]
return ans if ans else ''
回忆第一个步骤:right刚开始右移的时候,我们会移动到包含T中所有字母的时候才停下来。由于实在想不到怎么验证这件事,我就单独用一个字典remain来记录,如果remain空了就说明包含了。
然而看了别人有另一个方法,配合counts,使用一个整数remain就可以了;可以精简代码,减少空间复杂度。remain代表了在相对于T中的字母,窗口中还缺少的个数。要维护remain,当counts[key]是正数,并且在减少时,remain就跟着减少即可。
这是更改后的。其实还是比较繁杂。别人有更精简的,但是我觉得我这样我比较好理解。
from collections import Counter
class Solution:
def minWindow(self, s: str, t: str) -> str:
left = 0
counts = Counter(t)
remain = len(t)
ans = None
for right in range(len(s)):
curr = s[right]
if curr in counts.keys():
if counts[curr] > 0:
remain -= 1
counts[curr] -= 1
if remain == 0:
while left < right:
while s[left] not in counts.keys():
left += 1
counts[s[left]] += 1
if counts[s[left]] > 0:
counts[s[left]] -= 1
break
else:
left += 1
if not ans or len(ans) > right - left:
ans = s[left:right+1]
return ans if ans else ''