开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情
1. 今日语录
情人可以没有,力扣不能断刷。
2. 题目
最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果 s 中存在这样的子串,我们保证它是唯一的答案。
3. 思路
- 这虽然是一道hard,但其实思路是相对容易的,难点是细节上的处理。
- 滑动窗口类型的题目,其实都很相似。
- 一般都是 左指针l,右指针r 开始都为 0,然后 r 先移动,满足题目条件后,记录下当前结果,然后就是 l 开始移动,缩小窗口大小。
- 难点一般在于 l 的移动后 该有什么措施?
- 以本题为例,左侧l移动后,遇到了 t表中 出现的字符,这时窗口本身会立即多一个可用字符,这就推动了 r 继续向右移动。
- 易错点:一开始没考虑到 t 里的重复元素, 单纯用includes方法判断。
4. 代码
var s = "ADOBECODEBANC";
var t = "ABC";
var minWindow = function (s, t) {
// 根据数组t新建个map表,记录每个字符的出现个数
let map = new Map()
for (let ch of t) {
if (map.has(ch)) {
map.set(ch, map.get(ch) + 1)
} else {
map.set(ch, 1)
}
}
// 滑动窗口 [l,r]
let l = r = 0;
// 字符串的种类个数
let size = map.size;
let res = '';
while (r < s.length) {
if (map.has(s[r])) {
let n = map.get(s[r])
map.set(s[r], n - 1)
// 某个字符匹配完,剩余匹配数size -= 1
if (n === 1) {
size--;
}
}
// size == 0时,说明所有字符都匹配完
while (size === 0) {
let str = s.slice(l, r + 1)
// res 为 出现过的str 的 最小值
if (res) {
res = res.length <= str.length ? res : str;
} else {
res = str;
}
// 左侧开始滑动,一方面是试探性缩减窗口的大小
// 另一面是恢复size的可用个数,使窗口能推动下去
if (map.has(s[l])) {
const n = map.get(s[l])
map.set(s[l], n + 1)
// 某一字符从0到1,剩余匹配数size -= 1
if (n === 0) {
size += 1
}
}
l++;
}
r++;
}
return res;
};
console.log(minWindow(s, t))
5. 关键字
滑动窗口