leetcode 76. Minimum Window Substring的思路与python实现

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 ''
                

 

你可能感兴趣的:(leetcode)