LCR 017.最小覆盖子串
给定两个字符串 s
和 t
。返回 s
中包含 t
的所有字符的最短子字符串。如果 s
中不存在符合条件的子字符串,则返回空字符串 ""
。
如果 s
中存在多个符合条件的子字符串,返回任意一个。
注意: 对于 t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最短子字符串 "BANC" 包含了字符串 t 的所有字符 'A'、'B'、'C'
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入:s = "a", t = "aa"
输出:""
解释:t 中两个字符 'a' 均应包含在 s 的子串中,因此没有符合条件的子字符串,返回空字符串。
提示:
1 <= s.length, t.length <= 105
s
和t
由英文字母组成
**进阶:**你能设计一个在 o(n)
时间内解决此问题的算法吗?
法1:滑动窗口
分析:
使用一个map记录字符串t中各个字符出现的次数。
然后遍历字符串s,如果当前字符endCh是在map中的,那map对应字符键的值就减1,map中元素个数也相应减1;右指针右移,直到map中所有值都为0,说明当前窗口,包含了字符串t,此时记录最小长度,记录这时的索引起始位置。每次找到后,左指针需要右移一个,因为右移了,所以对应map的键需要加1,表明要在s字符串右边出要找到这个字符才行。
- 时间复杂度:
O(m + n)
,其中m
是字符串t
的长度,n
是字符串s
的长度。 - 空间复杂度:
O(m)
,其中m
是字符串t
的长度。
function minWindow(s, t) {
// 用来记录字符串 t 中每个字符的出现次数
const charToCount = new Map();
// 统计 t 中每个字符的频次
for (let ch of t) {
// 如果字符已经存在,累加其数量;否则设置为 1
charToCount.set(ch, (charToCount.get(ch) || 0) + 1);
}
let count = charToCount.size; // 记录 t 中不重复字符的种类数
let start = 0, end = 0; // 窗口的左右边界
let minStart = 0, minEnd = 0; // 最小窗口的左右边界
let minLength = Infinity; // 最小窗口的长度,初始化为 Infinity
// 开始滑动窗口
while (end < s.length || (count === 0 && end === s.length)) {
if (count > 0) {
// 扩展窗口:右边界向右移动
let endCh = s.charAt(end); // 获取当前右边界的字符
if (charToCount.has(endCh)) {
// 如果当前字符在 t 中,减少该字符的需求量
charToCount.set(endCh, charToCount.get(endCh) - 1);
// 如果字符需求量为 0,表示该字符已经完全匹配
if (charToCount.get(endCh) === 0) {
count--; // 需要匹配的字符种类数减少
}
}
end++; // 右边界向右移动
} else {
// 收缩窗口:左边界向右移动
if (end - start < minLength) {
// 如果当前窗口大小小于最小窗口大小,更新最小窗口
minLength = end - start;
minStart = start;
minEnd = end;
}
let startCh = s.charAt(start); // 获取当前左边界的字符
if (charToCount.has(startCh)) {
// 如果当前字符在 t 中,恢复该字符的需求量
charToCount.set(startCh, charToCount.get(startCh) + 1);
// 如果字符需求量恢复为 1,表示该字符还需要匹配
if (charToCount.get(startCh) === 1) {
count++; // 需要匹配的字符种类数增加
}
}
start++; // 左边界向右移动
}
}
// 如果找到了符合条件的子串,返回该子串;否则返回空字符串
return minLength < Infinity ? s.substring(minStart, minEnd) : "";
}
console.log(minWindow("ADOBECODEBANC","ABC"));
js语法还不太熟,记录一下:
-
charToCount.set(ch, (charToCount.get(ch) || 0) + 1);
-
charToCount.get(ch)
:从charToCount这个map中取出字符串ch对应的值,如果ch这个键不存在,则返回undefined
; -
(charToCount.get(ch) || 0)
: 如果ch这个键不存在,这个式子变为undefined || 0=0
,因为这里undefined
和0
都是fasle- 在 JavaScript 中,假值(falsy values)有以下几种:
false
0
""
(空字符串)null
undefined
NaN
- 在 JavaScript 中,假值(falsy values)有以下几种:
-
(charToCount.get(ch) || 0) + 1
:如果ch这个键不存在,当前这个ch是第一个计数,所以要加一。 -
charToCount.set(ch, (charToCount.get(ch) || 0) + 1)
:再将键值对重新写入charToCount中
-
-
charToCount.has(endCh)
map.has(key)
,返回一个布尔值:- 如果
Map
中存在该键,则返回true
。 - 如果
Map
中不存在该键,则返回false
。
- 如果