原题 s 字符串中含有 t 字符串所有字母的子串(可乱序),最短是哪个 题解参考
给定两个字符串 s 和 t,长度分别是 m 和 n,返回 s 中的 最短窗口 子串,使得该子串包含 t 中的每一个字符(包括重复字符)。如果没有这样的子串,返回空字符串* *""。
测试用例保证答案唯一。
示例 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.lengthn == t.length1 <= m, n <= 105s和t由英文字母组成
**进阶:**你能设计一个在 O(m + n) 时间内解决此问题的算法吗?
/*
当前函数是一张`map` 同时表示「还缺几个」和「多出来几个」,所以`map[c]` 可能变成负数,理解成本高。更直观的版本是:
`need` :`t` 里每个字符需要几个
`window` :当前窗口里每个字符已有几个
`valid` :已有数量满足需求的字符种类数
*/
const minWindow = function (s, t) {
if (s.length === 0 || t.length === 0) return '';
const need = {};
const window = {};
for (const c of t) {
need[c] = (need[c] || 0) + 1;
}
const needKinds = Object.keys(need).length;
let valid = 0;
let left = 0;
let start = -1;
let minLen = s.length + 1;
for (let right = 0; right < s.length; right++) {
const c = s[right];
if (need[c] !== undefined) {
window[c] = (window[c] || 0) + 1;
if (window[c] === need[c]) {
valid++;
}
}
while (valid === needKinds) {
// 判断边界条件
const len = right - left + 1;
if (len < minLen) {
minLen = len;
start = left;
}
// 窗口左移
const d = s[left];
if (need[d] !== undefined) {
if (window[d] === need[d]) {
valid--;
}
window[d]--;
}
left++;
}
}
return start === -1 ? '' : s.substring(start, start + minLen);
};const minWindow = function (s, t) {
let minLen = s.length + 1;
let start = s.length;
let map = {};
let missingType = 0;
for (const c of t) {
if (!map[c]) {
missingType++;
map[c] = 1;
} else {
map[c]++;
}
}
let l = 0,
r = 0;
for (; r < s.length; r++) {
let rightChar = s[r];
if (map[rightChar] !== undefined) {
map[rightChar]--;
}
// 也就是遍历到这一步的时候,所有的数字都找到了,这时候右边界已经确定了
// 然后开始通过下面的 while 来找到左边界
if (map[rightChar] === 0) {
missingType--;
}
while (missingType === 0) {
if (r - l + 1 < minLen) {
minLen = r - l + 1;
start = l;
}
// 还是没有明白这两个 if 条件语句是要用来干嘛
// 应该是用来判断退出 while 的边界条件
// 也就是不断将左边界右移,一旦发现右移的时候,遇到了需要匹配的字符串,就中断
// 也不是说遇到了匹配的字符串就中断,因为如果全部拿到了匹配的字符串后,部分字符串可能匹配的次数多余需要匹配的次数
// 所以在上面有 map[rightChar]--,可能到最终 map[rightChar] 是个负数
// 所以在这里我们需要保证次数,通过map[leftChar]++,如果map[leftChar]大于0了就退出
/*
是因为上面是当 missingType === 0 进入时候的计算,然后需要继续往后走,所以这里主要的是 l++,然后 l++ 之后,会带来什么情况
也就是在当前字符串中,如果去掉了 s[l] 会有什么情况,这两个 if 就是在计算这种情况
*/
let leftChar = s[l];
if (map[leftChar] !== undefined) {
map[leftChar]++;
}
if (map[leftChar] > 0) {
missingType++;
}
l++;
}
}
if (start === s.length) {
return '';
}
return s.substring(start, start + minLen);
};
let s = 'ADOBECODEBANC',
t = 'ABC';
let result = minWindow(s, t);
console.log(result);