cstring判断是否包含子串_leetcode76. 最小覆盖子串

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

优化:

思路:

上面方法是否有地方可以优化呢?

答案是肯定的,上面的方法在每次判断是否符合条件的时候,都要调用子函数,对两个哈希表进行遍历比较,每次比较时间复杂度达到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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值