【算法】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]

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