LeetCode热题(JS版) -76. 最小覆盖子串

231 阅读3分钟

题目

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果 s 中存在这样的子串,我们保证它是唯一的答案。  

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A''B''C'

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

m == s.length
n == t.length
1 <= m, n <= 105
s 和 t 由英文字母组成

进阶:你能设计一个在 o(m+n) 时间内解决此问题的算法吗?

思路

本题解采用滑动窗口的思路进行解答。具体步骤如下:

  • 定义两个指针 left 和 right,分别指向字符串 S 的首尾位置。
  • 定义两个哈希表 need 和 window,其中 need 存储字符串 T 中每个字符的数量,window 存储滑动窗口中每个字符的数量。
  • 初始化 left、right 和 count 变量,其中 count 表示滑动窗口中已包含 T 中字符的数量。
  • 向右移动 right 指针,如果当前字符在 need 中,则将 window 对应字符数量加 1。如果 window 中对应的字符数量等于 need 中的数量,则将 count 加 1。
  • 当 count 等于字符串 T 的长度时,保存当前的子串,并将 left 指针向右移动,直到左侧的字符不在 T 中。此时仍需要将 window 对应字符数量减 1,并将 count 减 1。

重复步骤 4~5,直到 right 指针到达字符串 S 的末尾。

关键是滑动和收缩

function minWindow(s: string, t: string): string {
    // 先用map记录t字串的每个字符的数量。并初始化窗口中对应的字符
    const need = {};
    const window = {};
    for (let c of t) {
        if (!need[c]) need[c] = 0;
        if (!window[c]) window[c] = 0;

        need[c] ++;
    }

    let left = 0, right = 0;// 记录窗口范围
    let count = 0; // 记录有几个字符包含了
    let start = 0, len = Infinity;// 记录最小目标子串的起始位置和长度

    // 窗口右移
    while(right < s.length) {
        const c1 = s[right];
        if(need[c1]) {
            window[c1]++;// 窗口中找到一个有效字符
            if(need[c1] === window[c1]) count ++;// 刚好该字符已经全部覆盖了
        }

        // 滑动
        right++;

        // 找到了就收缩
        while(count === Object.keys(need).length) {
            // 有更短
            const diff = right - left;
            if(diff < len) {
                len = diff;
                start = left;
            }

            // 尝试收缩窗口
            const c2 = s[left];
            if(need[c2]) {
                window[c2]--;
                if(window[c2] < need[c2]) count--// 收缩后覆盖的字符数可能受到影响
            }

            // 右移
            left++;
        }
    }

    return len === Infinity ? '' : s.substr(start, len);
};

image.png

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

总结

本题解使用滑动窗口的思路进行解答,并使用哈希表存储字符数量信息,使得时间复杂度降低至 O(n)。