Python版-LeetCode 学习:76. 最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。

示例:输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring

方法1:该方法主要使用的是滑动窗口思想,大概含义:

用left,right表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度right-left  +1,这些长度中的最小值就是要求的结果

具体实现过程如下:

(1)计算目标值t中每个char的个数,作为char的需求清单need,即需要need中k*v个char;

         用处:当检索到滑动窗口中存在一个char时,就抵消掉need中对应的char值,即need[char] -1 ;当检索到窗口函数中不存在一个char时,就增加need中对应的char值,即need[char] +1 ;

(2)记录目标值t的长度,needCnt,即总共需要的字符串数,needCnt等于need中value的总和。

         用处:取代遍历need,通过总数来判断滑动窗口是否包含了所有的t中char

(3)不断增加指针right使滑动窗口增大,直到窗口包含了T的所有元素:当窗口包含了所有的need字符(即needCnt=0),意味着可以剔除窗口括进来的不需要字符,这时可以缩小窗口了

(4)不断增加left,使滑动窗口缩小。因为是要求最小字串,所以将不必要的元素排除在外,使长度减小,直到碰到一个必须包含的元素(其实是清单need中必须包含的子串不能小于0,当=0时,意味着需求刚好满足),这个时候不能再扔了。再扔就不满足条件了,记录此时滑动窗口的left和right的值,right-left  +1,即是最小子串的长度。并保存最小值。

(5)当right再增加一个位置,滑动窗口就不满足条件的时候,这是就要移动left,是滑动窗口增大,直至窗口包含了所有的need字符(即needCnt=0)。这时从步骤(3)从新来,寻找新的满足条件的滑动窗口,如此反复,直到left超出了字符串S范围。

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 目标字符记录
        need=collections.defaultdict(int)
        for c in t:
            need[c]+=1
        # 目标字符长度
        needCnt=len(t)
        left=0
        res=(0,float('inf'))
        for r,c in enumerate(s):
            if need[c]>0:
                # 如果need[c]>0,意味着窗口搜索到目标值
                needCnt-=1
            # 目标值被搜索到,则need相应value-1,即用以记录搜索到need中字符的个数;
            # =0代表搜索到的值和need值相同;<0代表need中同一个字符被多次搜索到;
            # >0,代表need中该字符没有全部被遍历出来
            need[c]-=1   
            
            if needCnt==0: # 滑动窗口包含了所有T元素  
                # 开始窗口函数内部的搜索
                while True:  # 移动滑动窗口右边界i,排除多余元素
                    c=s[left] 
                    if need[c]==0:  
                        # need[c]==0,break 情况,代表着这轮的滑动窗口不符合要求
                        break
                    # 窗口中字符和need相互抵消
                    need[c]+=1
                    r+=1
                if r-left <res[1]-res[0]:   #记录结果
                    res=(left,r)
                # 窗口函数内部计算完后,向下一步迭代
                # s[left]是目标集中t的字符,这时从窗口函数清除,
                # 则意味着窗口函数需要新的字符填充,即need[s[left]]和needCnt需要加一
                need[s[left]]+=1  # left增加一个位置,寻找新的满足条件滑动窗口
                needCnt+=1
                # 新一轮的右边界
                r+=1
        return '' if res[1]>len(s) else s[res[0]:res[1]+1]    #如果res始终没被更新过,代表无满足条件的结果

总结:该类题目主要就是利用left和right俩套指针循环,形成一个可滑动的窗口,将目标值t与滑动窗口进行比较。主要的难点也在如何进行滑动窗口和目标值的比较。我们利用记账簿的形式,将需要的字符记录为字符整数集need,每满足一个目标值就减1.在这个过程中,我们还记录了滑动窗口中的每个字符,这些不需要的字符都记为负值。当他们被移除滑动窗口时,这些字符就记为0.那些从滑动窗口添加进need的char,其中不是目标值的char的value永远都不会为正。

当need中所有的字符value都小于等于0时,即需求被满足了。而维护一个额外的变量needCnt的作用是,来记录所需元素的总数量,当我们碰到一个所需元素c,不仅need[c]的数量减少1,同时needCnt也要减少1,这样我们通过needCnt就可以知道是否满足条件,这样就无需遍历need字典了。
前面也提到过,need记录了遍历到的所有元素,不是目标值的char的value永远都不会为正,而只有need[c]>0时,代表c就是所需元素,当need中所有char的value都是0时,则代表所有的目标值都被满足,且滑动窗口中没有多余的字符。
链接:https://leetcode-cn.com/problems/minimum-window-substring/solution/tong-su-qie-xiang-xi-de-miao-shu-hua-dong-chuang-k/

另参考:

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need = collections.defaultdict(int)
        for c in t:
            need[c] += 1
        needCnt = len(t)
        i = 0 #记录起始位置
        res = (0, float('inf'))  #用两个元素,方便之后记录起终点
        #三步骤:
        #1. 增加右边界使滑窗包含t
        for j,c in enumerate(s):
            if need[c] >0:
                needCnt -= 1
            need[c] -= 1 #这行放在外面不可以,看19行 need[c] == 0
        #2. 收缩左边界直到无法再去掉元素   !注意,处理的是i
            if needCnt == 0:
                while True:
                    c = s[i]
                    if need[c] == 0: #表示再去掉就不行了(need>0)
                        break
                    else:
                        need[c] += 1
                        i += 1
                if j-i < res[1] - res[0]:  #这里是否减一都可以,只要每次都是这样算的就行,反正最后也是输出子串而非长度
                    res = (i,j)
        #3. i多增加一个位置,准备开始下一次循环(注意这步是在 needCnt == 0里面进行的 )
                need[s[i]] += 1
                needCnt += 1    #由于 移动前i这个位置 一定是所需的字母,因此NeedCnt才需要+1
                i += 1
        return "" if res[1]>len(s) else s[res[0]: res[1]+1]

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
回答: 最长回文子串可以通过两种方法来实现。第一种是使用中心扩展法,代码如下: ```python class Solution: def check(self, s, l, r): while l >= 0 and r < len(s) and s[l == s[r]: l -= 1 r += 1 return l + 1, r - 1 def longestPalindrome(self, s: str) -> str: start, end = 0, 0 for x in range(len(s)): l1, r1 = self.check(s, x, x) l2, r2 = self.check(s, x, x + 1) if r1 - l1 > end - start: start, end = l1, r1 if r2 - l2 > end - start: start, end = l2, r2 return s[start:end+1] ``` 第二种方法是使用动态规划,代码如下: ```python class Solution: def longestPalindrome(self, s: str) -> str: res = '' for i in range(len(s)): start = max(0, i - len(res) - 1) temp = s[start:i+1] if temp == temp[::-1]: res = temp else: temp = temp<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [5. 最长回文子串Python 实现)](https://blog.csdn.net/d_l_w_d_l_w/article/details/118861851)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [LeetCodePython3)5.最长回文子串](https://blog.csdn.net/weixin_52593484/article/details/124718655)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [力扣 (LeetCode)刷题笔记5.最长回文子串 python](https://blog.csdn.net/qq_44672855/article/details/115339324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值