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 ""
    }
}

你可能感兴趣的:(LeetCode)