leetcode(力扣) 76. 最小覆盖子串 (滑动窗口,超详细问答版解析)

文章介绍了如何使用滑动窗口和哈希表解决给定字符串s中涵盖字符串t所有字符的最小子串问题。通过初始化哈希表记录t的字符需求,动态调整窗口边界,找到满足条件的最小子串。关键在于正确处理count的减少时机和左边界移动的终止条件。
摘要由CSDN通过智能技术生成

题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

示例 2:
输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。

示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

思路分析

一道滑动窗口的题,不过归到了hard里,显然是有点弯弯绕绕在里面。

按步骤走先。

步骤一:

划定窗口的左右边界,left和right。
设置一个变量count来记录当前所需的字符。初始时count为字符串t的长度。

步骤二:

不断往右移动right,当count为0时,则表示当前窗口内的元素已经符合条件。

步骤三:

将left往右移动,减小窗口内的字符数量,将不必要的值扔掉,毕竟要求是最小的窗口长度嘛。

上述三步就是我一开始的思路,虽然我知道这道题肯定有点坑,没那么简单,但是还是按照上面这个思路写了一下,发现几个问题。

问题1:何时让count减一?

是碰到需要的字符就减1吗,显然不是,比如 s = ‘BECODEBANC’ t = ‘ABC’ 。count初始值为3。
可以看到,right不断从0开始往右走。当需要的字符A还没出现的时候,就已经出现了两次B和一次C了,这时候count已经为0。但此时窗口内的元素并不符合要求。
所以,需要一个辅助的哈希表来记录所需元素是否都已经符合要求了。
初始设置一个哈希表hs,遍历t来设置hs,本例中hs={‘A’: 1, ‘B’: 1, ‘C’: 1}。
right不断往右的过程中,遇到字符先判断hs里该值对应的value是否大于0,如果大于0,则表示是我们需要的值,此时再让count减一。
用上这个方法之后,再回头看刚才的例子。当出现第一个B的时候,他是我们需要的字符,则让hs[’ B ']-=1,计数器 ‘count-=1‘。此时哈希表中B字符对应的value就等于0了。当再次遇到第二个B的时候,哈希表中的B对应value为0,不满足大于0的要求,所以不减count。

问题2: 当count为0时,立即记录窗口长度值吗?

在第二步的时候,count为0了,虽然此时窗口内的长度已经覆盖了t,但是并不是要立即记录窗口的长度值。 比如 s = ‘BECODEBANC’ t = ‘ABC’

假设现在的left指向第一个B,也就是0号位置,right不断向右走,走到倒数第三个位置的A,此时窗口内包含ABC三个元素,这时候count=0了。但可以发现,窗口内含有两个B,左边的B是不必要的值。
为了减小窗口的长度符合题意,则尽量将左边界往右移动,缩短窗口长度。
此时就遇到了问题3.

问题3,:左边界left缩短的终止条件是什么?

从问题2可以看到,缩短左边界是在每一次记录前要做的事。
在解决这个问题之前,必须重申,hs表里记录的是每一个元素需要的次数。
比如 {‘A’: 1, ‘B’: 1, ‘C’: 1}),则表示A需要1次,B需要1次,C需要1。
如果{‘A’: 0。,C=‘-1’} 则表示A已经不需要了,刚好,而C多余一个。

hs表里记录的是每一个元素需要的次数!!!
hs表里记录的是每一个元素需要的次数!!!
hs表里记录的是每一个元素需要的次数!!!

牢记上面这句话。

所以在判断何时终止左边界的时候就非常好想了,如果碰到左边界的值在哈希表中的value为0了,则说明这个左边界的值是必须需要的,此时终止。

这时候left和right的窗口内所有元素都是需要的,此时记录,更新窗口的最短长度。

问题4:非条件值是否会影响left的终止条件?

本例中必须条件值是ABC,还有一个非条件值,DEON四个。
在程序运行过程中,非条件值也会被加入到哈希表中,那么DEON这四个非条件值会影响left的终止吗。
显然不会,因为哈希表本身就是记录所需元素的个数的,一开始,那些不需要的元素是没有进行初始化的,或者说初始化为0,遇到一个则变成-1。再看一遍那句话hs表里记录的是每一个元素需要的次数!!!秒懂!!结束。

完整代码

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        import collections
        hs = collections.defaultdict(int)
        for i in t: # 先初始化t字符到哈希表中
            hs[i] +=1
        count = len(t)
        res = [0,99999]
        left = 0
        right = 0
        while right<len(s):
            if hs[s[right]]>0: # 若哈希表中当前value大于0,则只可能是我们需要的字符
                count-=1
            hs[s[right]]-=1
            if count == 0:
                while True: # 去掉左边多余的值
                    if hs[s[left]] == 0:
                        break
                    hs[s[left]] +=1
                    left +=1
                if right-left < res[1] - res[0]:  # 记录当前最大值
                    res[0], res[1] = left,right
                # 这时候的字符串状态是满足条件的,要移动左指针让他继续往右移动
                hs[s[left]] +=1
                count +=1
                left+=1
            right +=1
        return ''if res[1]==99999 else s[res[0]:res[1]+1]
       
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值