LeetCode-76: Minimum Window Substring(最小覆蓋子串)

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

Example:

Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"

Note:

If there is no such window in S that covers all characters in T, return the empty string “”.
If there is such window, you are guaranteed that there will always be only one unique minimum window in S.

思路

  • 本題爲從S串中找到包含T串中所有字符的最小窗口,可通過滑動窗口的算法來實現。
  • 需要注意的是,這裏要匹配的是<字符,個數>,不是僅僅字符;另外由於輸入僅包含字母,因此構建哈希表的時候可以使用128位的數組來實現,即使用字母的ASCII碼來作爲鍵。具體步驟如下:
    1. 使用left和right兩個指針作爲窗口的左右邊界;
    2. 擴展窗口:通過移動right來擴展窗口,並向窗口中增加元素,同時記錄匹配到的“KV對”的個數(即match)
    3. 縮減窗口:當窗口中的“KV對”的個數滿足目標時(即match == target),通過移動left來縮減窗口,並減少窗口中的元素,同時記錄match的變化,當匹配到的“KV對”的個數不滿足時(即match != target),繼續擴展窗口,直到到達S串的末尾。

Java實現

class Solution {
    public String minWindow(String s, String t) {
        // 不符合的輸入直接返回
        if (s == null || s == "" || t == null || t == "" || s.length() < t.length()) {
            return "";
        }
        // 構建用於窗口串和模式串的哈希表,由於輸入中只包含字母,使用128位的數組來代替
        int[] window = new int[128];
        int[] pat = new int[128];
        // 存儲窗口的左右指針
        int left = 0;
        int right = 0;
        // 存儲窗口中匹配到的KV數量
        int match = 0;
        // 存儲需要匹配的KV數量
        int target = 0;
        // 存儲最小窗口的結果
        int minWindow = s.length() + 1;
        int leftRes = 0;
        int rightRes = 0;
        // 標記是否匹配成功過
        boolean flag = false;

        // 構建目標字符集合
        for (char c : t.toCharArray()) {
            if (pat[(int) c] == 0) {
                target ++;
            }
            pat[(int) c] ++;
        }

        // 滑動窗口找覆蓋子串
        while (right < s.length()) {
            /****************擴展窗口*************/
            char r = s.charAt(right);
            // 向窗口中增加元素
            window[(int) r] ++;
            // 如果KV對能匹配上,則 match++
            if (window[(int) r] == pat[(int) r]) {
                match ++;
            }

            /****************精簡窗口*************/
            while (match == target) {
                flag = true;
                // 判斷大小
                if (right-left+1 < minWindow) {
                    minWindow = right-left+1;
                    leftRes = left;
                    rightRes = right;
                }

                char l = s.charAt(left);

                // 如果left屬於目標KV,則更新match
                if (pat[(int) l] != 0 && pat[(int) l] == window[(int) l]) {
                    match --;
                }

                // 縮減窗口
                window[(int) l] --;
                left ++;
            }
            right ++;
        }
        return flag ? s.substring(leftRes, rightRes+1) : "";
    }
}

Python實現

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        # 定義兩個哈希表來存放window和pat,可以使用128位的數組
        window = [0 for _ in range(128)]
        pat = [0 for _ in range(128)]
        # 定義輸出目標
        minWindow = len(s) + 1
        leftRes = 0
        rightRes = 0
        # 定義left和right來標識窗口的左右指針
        left = 0
        right = 0
        # 定義一個標記,標識是否匹配成功過
        flag = False
        # 定義變量match:已匹配的KV 和 target:目標匹配KV
        match = 0
        target = 0
        # 構建pat,並計算target
        for c in t:
            if pat[ord(c)] == 0:
                target += 1
            pat[ord(c)] += 1

        # 滑動窗口計算最小窗口
        while right < len(s):
            # 擴展窗口:放入元素,計算match
            r = ord(s[right])
            # 放入元素
            window[r] += 1
            # 計算match
            if pat[r] != 0 and window[r] == pat[r]:
                match += 1
            # 當match == target 縮減窗口
            while match == target:
                # 首先更新目標
                flag = True
                if right - left + 1 < minWindow:
                    minWindow = right - left + 1
                    leftRes = left
                    rightRes = right
                # 縮減窗口:先更新match,後從窗口中取出元素
                l = ord(s[left])
                # 更新matches
                if pat[l] != 0 and window[l] == pat[l]:
                    match -= 1
                # 取出元素
                window[l] -= 1
                # 縮減窗口
                left += 1
            # 擴展窗口
            right += 1
        return s[leftRes : rightRes + 1] if flag else "" 

Scala實現

object Solution {
    def minWindow(s: String, t: String): String = {
        // 定義兩個哈希表來存放window和pat,可以使用128位的數組
        val window = new Array[Int](128)
        val pat = new Array[Int](128)
        // 定義輸出目標
        var minWindow = s.length + 1
        var leftRes = 0
        var rightRes = 0
        // 定義left和right來標識窗口的左右指針
        var left = 0
        var right = 0
        // 定義一個標記,標識是否匹配成功過
        var flag = false
        // 定義變量match:已匹配的KV 和 target:目標匹配KV
        var matches = 0
        var target = 0

        // 構建pat,並計算target
        for (c <- t.toCharArray) {
            // 如果首次出現
            if (pat(c) == 0) {
                target += 1
            }

            pat(c) += 1
        }

        // 滑動窗口計算最小窗口
        while (right < s.length) {
            // 擴展窗口:放入元素,計算matches
            val r = s.charAt(right)
            // 放入元素
            window(r) += 1
            // 計算matches
            if (pat(r) != 0 && pat(r) == window(r)) {
                matches += 1
            }

            // 當matches == target 縮減窗口
            while (matches == target) {
                // 首先更新目標
                flag = true
                if (right - left + 1 < minWindow) {
                    minWindow = right - left + 1
                    leftRes = left
                    rightRes = right
                }

                // 縮減窗口:先更新matches,後從窗口中取出元素
                val l = s.charAt(left)

                // 更新matches
                if (pat(l) != 0 && pat(l) == window(l)) {
                    matches -= 1
                }

                // 取出元素
                window(l) -= 1

                left += 1
            }

            right += 1
        }

        return if (flag) s.substring(leftRes, rightRes + 1) else ""
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章