这是跟着代码随想录的顺序学习算法的第?天。(二刷)
以下是学习时自己的一些理解与笔记,如有错误欢迎指正与讨论。
76. 最小覆盖子串
参考相关链接:
笔记
解本题的主要方法是,滑动窗口 + Map。
主要思路:
-
left、right
记录当前窗口的两端 -
map
记录t
中各个字符的被覆盖情况,即键名为每个字符
,对应的键值为目前滑动窗口中覆盖 t 所需要的该字符的数量
,这意味着键值是有可能出现负数的情况,具体表示的含义就是当前滑动窗口中关于该字符的数量是有多余的(也就是数量足够覆盖t
的对应字符,且有多余) -
temp
用于记录t
的未被覆盖的字符数量 -
res
用于记录结果
-
扩大滑动窗口,即移动
right
,并进行判断,如果s[right]
是t
所需要的字符,则变化对应的map
的键值,并根据当前的字符是否为多余的(
map.get(s[right]) > 0
则不是多余的),来改变temp
的大小。 -
当滑动窗口中的字符串足以覆盖
t
时候(即temp === 0
),收缩left
直到当前的滑动窗口不足以覆盖t
即(即temp > 0
),值得一提的是,这里的temp
在多余的字符串数量发生变化时,不做改变。 -
收缩时,根据需要
res[1] - res[0] > right - left
,记录当前的滑动窗口两端的值[res[0], res[1]] = [left, right]
-
最后,根据
res
记录的滑动窗口情况,返回对应的结果。
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
// s 中 涵盖 t 所有字符的 最小子串
// 否则 返回 ""
// map中的每个值都小于 0
// 记录是否满足
// 从正数减小才算数
const map = new Map();
let left = 0, right = 0, temp = t.length, res = [0, Infinity];
for(const x of t) { // 记录 t 各字母的数量
if(map.has(x)) {
map.set(x, map.get(x) + 1);
} else {
map.set(x, 1);
}
}
for(; right < s.length; right++) {
// 填入 s[right]
if(map.has(s[right])) {
// 该字母未填满
if(map.get(s[right]) > 0) {
temp--;
}
map.set(s[right], map.get(s[right]) - 1);
} else continue;
// 未填满 t 继续填
// 填满 t 减长度 移动 left 直到 temp > 0
while(temp === 0 && left <= right) {
// 记录最短覆盖子串
if(res[1] - res[0] > right - left) {
[res[0], res[1]] = [left, right];
}
if(map.has(s[left])) {
// 该字母无多余数量 即 大于等于 0
if(map.get(s[left]) >= 0) {
temp++;
}
map.set(s[left], map.get(s[left]) + 1);
}
left++;
}
}
return res[1] === Infinity ? "" : s.slice(res[0], res[1] + 1);
};