题目描述
给你一个字符串 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 的子串中,
因此没有符合条件的子字符串,返回空字符串。
思路&js代码
1、滑动窗口
function isCovered(cntS, cntT) {
for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
for (let i = 'a'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) {
if (cntS[i] < cntT[i]) {
return false;
}
}
return true;
}
var minWindow = function(s, t) {
const cntS = Array(128).fill(0); // s 子串字母的出现次数
const cntT = Array(128).fill(0); // t 中字母的出现次数
for (const c of t) {
cntT[c.codePointAt(0)]++;
}
const m = s.length;
let ansLeft = -1, ansRight = m;
let left = 0;
for (let right = 0; right < m; right++) { // 移动子串右端点
cntS[s[right].codePointAt(0)]++; // 右端点字母移入子串
while (isCovered(cntS, cntT)) { // 涵盖
if (right - left < ansRight - ansLeft) { // 找到更短的子串
ansLeft = left; // 记录此时的左右端点
ansRight = right;
}
cntS[s[left].codePointAt(0)]--; // 左端点字母移出子串
left++;
}
}
return ansLeft < 0 ? "" : s.substring(ansLeft, ansRight + 1);
};2、优化
var minWindow = function(s, t) {
const cnt = Array(128).fill(0);
let less = 0;
for (let c of t) {
c = c.codePointAt(0);
if (cnt[c] === 0) {
less++; // 有 less 种字母的出现次数 < t 中的字母出现次数
}
cnt[c]++;
}
const m = s.length;
let ansLeft = -1, ansRight = m;
let left = 0;
for (let right = 0; right < m; right++) { // 移动子串右端点
const c = s[right].codePointAt(0); // 右端点字母
cnt[c]--; // 右端点字母移入子串
if (cnt[c] === 0) {
// 原来窗口内 c 的出现次数比 t 的少,现在一样多
less--;
}
while (less === 0) { // 涵盖:所有字母的出现次数都是 >=
if (right - left < ansRight - ansLeft) { // 找到更短的子串
ansLeft = left; // 记录此时的左右端点
ansRight = right;
}
const x = s[left].codePointAt(0); // 左端点字母
if (cnt[x] === 0) {
// x 移出窗口之前,检查出现次数,
// 如果窗口内 x 的出现次数和 t 一样,
// 那么 x 移出窗口后,窗口内 x 的出现次数比 t 的少
less++;
}
cnt[x]++; // 左端点字母移出子串
left++;
}
}
return ansLeft < 0 ? "" : s.substring(ansLeft, ansRight + 1);
};