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
s
和t
由英文字母组成
进阶:你能设计一个在 O(m+n)
时间内解决此问题的算法吗?
出处:LeetCode
解法一:滑动窗口
思路
我们可以使用滑动窗口来解决这个问题。我们可以维护两个指针 left
和 right
,分别指向滑动窗口的左右边界。我们可以使用哈希表 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);
}
复杂度分析
- 时间复杂度:
- 空间复杂度: