跳到主要内容

76. 最小覆盖子串

题目描述

给你一个字符串 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 的子串中,因此没有符合条件的子字符串,返回空字符串。

提示:

  • 1 <= s.length, t.length <= 105
  • st 由英文字母组成

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

出处LeetCode

解法一:滑动窗口

思路

我们可以使用滑动窗口来解决这个问题。我们可以维护两个指针 leftright,分别指向滑动窗口的左右边界。我们可以使用哈希表 need 来存储字符串 t 中的字符及其出现的次数,使用哈希表 window 来存储滑动窗口中的字符及其出现的次数。

在每一步中,我们需要移动右指针 right,直到滑动窗口中包含了字符串 t 中的所有字符。接着,我们需要移动左指针 left,直到滑动窗口中不再包含字符串 t 中的所有字符。在每一步中,我们需要更新滑动窗口中的字符及其出现的次数。

代码

function minWindow(s: string, t: string): string {
const need: Map<string, number> = new Map();
const window: Map<string, number> = new Map();
let left = 0;
let right = 0;
let valid = 0;
let start = 0;
let len = Number.MAX_SAFE_INTEGER;

for (let i = 0; i < t.length; i++) {
const c = t[i];
need.set(c, (need.get(c) || 0) + 1);
}

while (right < s.length) {
const c = s[right];
right++;

if (need.has(c)) {
window.set(c, (window.get(c) || 0) + 1);
if (window.get(c) === need.get(c)) {
valid++;
}
}

while (valid === need.size) {
if (right - left < len) {
start = left;
len = right - left;
}

const d = s[left];
left++;

if (need.has(d)) {
if (window.get(d) === need.get(d)) {
valid--;
}
window.set(d, window.get(d)! - 1);
}
}
}

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

复杂度分析

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