【算法】leetcode 76. 最小覆盖子串(滑动窗口)

问题来源

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

示例:
    输入: S = "ADOBECODEBANC", T = "ABC"
    输出: "BANC"

说明:
    如果 S 中不存这样的子串,则返回空字符串 ""。
    如果 S 中存在这样的子串,我们保证它是唯一的答案。

大佬解析


代码


"""
需求:
    给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
     带着一张采购清单 去买菜!!!
    
思想:
    1.双指针+滑动窗口
    
    滑动窗口的思想:
        先寻求一个可行的解
        在可行解的基础上,优化这个可行解
        优化到不能优化,产生出一个可能的最优解
        继续寻求新的可行解
        继续优化这个可行解
        ……
        在可能的最优解中产生出最优解
    
    穷举是一根筋,滑动窗口法懂得做选择
        每一个字符最多被遍历 2 次,即被左、右指针分别走过一次
        但穷举法,有的节点被遍历次数显然超过 2
        如果说穷举扫描是一根筋,那滑动窗口法,就懂得做选择,去掉了重复的工作

    
"""


class Solution:
    def minWindow(self, s: str, t: str) -> str:
        """❤组织思路写代码
        需要一个变量,控制窗口的扩张/收缩
            窗口的扩张/收缩 取决于 —— 当前窗口是否找齐所有目标字符
            引入 missingType 变量,表示:当前缺失的字符种类数(还要找齐几种字符)
            遍历前,它的初始值为 t 字符串的字符种类数
            当 missingType == 0 时,代表没有缺少种类,当前窗口找齐了所有目标字符
        是否找齐了字符 取决于 各个字符的缺失情况
            用一个 HashMap 存储各个目标字符,和它对应的缺失个数
            t = 'baac' ,则 map 初始值为 { a: 2, b: 1, c: 1 }
            map 中的值是 动态变化 的,比如窗口新纳入一个 a ,则 map['a'] 值 -1
            map['a'] 为 0 代表不缺 a 字符了,找齐了
        好比 带着一张采购清单 去买菜
            想要买齐 萝卜、青菜、猪肉 若干
            买了萝卜 1 个,萝卜的未买数目就减 1 ,减到 0 了就是萝卜买齐了
            萝卜买齐了,未买种类就减 1 ,未买种类减到 0 了就是所有都买齐了
        其他几个变量
            left、right 指针,滑动窗口的左右端,初始值都为 0
            minLen :保存最小长度,初始值为正无穷,为了让第一次一定被改写
            resL : 记录最小子串的起始点
        核心思路
            主线是 right++ 即扩张窗口,右指针超出 s 串就停止循环
            右指针指向的新字符,如果是目标字符,就代表它的缺失个数 - 1
            缺失的个数如果因此变 0 ,则缺失的种类就 - 1
            扩张窗口,直到当前窗口包含了所有字符,此时 missingType === 0
            收缩窗口,并且只要不影响当前窗口包含所有字符,就一直收缩
            收缩带来产生最小串的可能,计算长度,与 minLen 比较
            左指针指向的字符,如果是目标字符,它会被舍弃,它的缺失个数 + 1
            缺失个数如果因此 > 0 ,缺失的种类数 + 1
            右指针步进 1 ,收缩窗口
        """
        # if len(t) > len(s):
        #     return ''
        resL = -1
        min_len = len(s) + 1
        map = {}
        missing_type = 0

        # 确定所需清单(各个字母出现次数)
        for i in t:
            if i in map:
                map[i] += 1
            else:
                missing_type += 1
                map[i] = 1

        # 双指针滑动窗口主逻辑部分
        left = 0
        for right in range(len(s)):  # right++ 扩张窗口,超出s串就结束循环
            right_char = s[right]
            if right_char in map:
                map[right_char] -= 1
                if map[right_char] == 0:
                    missing_type -= 1
            while missing_type == 0:
                if min_len > right - left + 1:
                    min_len = right - left + 1
                    resL = left
                left_char = s[left]
                if left_char in map:
                    map[left_char] += 1
                    if map[left_char] > 0:
                        missing_type += 1
                left += 1
        return '' if resL == -1 and min_len == (len(s) + 1) else s[resL:resL + min_len]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章