LeetCode76 最小覆盖子串
题目
解题:滑动窗口+双指针
解题思路见 最小覆盖子串,可以用 matchSuccess 来官方解答里的 check 函数,避免每次都要进行一一比较。
tFreq.size 后面不加括号。
// javascript
var minWindow = function(s, t) {
const sFreq = new Map(), tFreq = new Map();
// tFreq 记录 t 所有字符频数
for (const tEle of t) {
tFreq.set(tEle, (tFreq.get(tEle) || 0) + 1);
}
const sLen = s.length;
let l = 0, r = 0, ansL = -1, distance = sLen + 1;
let matchSuccess = 0;
while (r < sLen) {
// 当前字符在 tFreq 的字符中,更新窗口统计
if (tFreq.has(s[r]) === true) {
sFreq.set(s[r], (sFreq.get(s[r]) || 0) + 1);
if (sFreq.get(s[r]) === tFreq.get(s[r])) {
matchSuccess += 1;
}
}
// 直到 s 涵盖 t 所有字符 且 l <= r 时:开始收缩窗口
while (matchSuccess === tFreq.size && l <= r) {
// 更新最小 distance 和 起始位置 ansL
if (r - l + 1 < distance) {
distance = r - l + 1;
ansL = l;
}
// 下一步要移除 l 所以如果 tFreq.has(s[l]) 是 true,sFreq 对应的字符频数要减少
if (tFreq.has(s[l]) === true) {
sFreq.set(s[l], sFreq.get(s[l]) - 1);
if (sFreq.get(s[l]) < tFreq.get(s[l])) {
matchSuccess -= 1;
}
}
// 移动窗口的左边界
l++;
}
// 移动窗口的右边界
r++;
}
return ansL === -1 ? "" : s.substr(ansL, distance);
};
时间复杂度:最坏情况下左右指针对 s s s 的每个元素各遍历一遍, t t t 的每个元素也会被遍历一遍, O ( ∣ s ∣ + ∣ t ∣ ) O(∣s∣+∣t∣) O(∣s∣+∣t∣);
空间复杂度:这里用了两张哈希表作为辅助空间,每张哈希表最多不会存放超过字符集大小的键值对,我们设字符集大小为 C C C ,则渐进空间复杂度为 O ( C ) O(C) O(C)。
也可以 用一个哈希表,统计 t 中字符频数时 --
,并且用此哈希表记录 s[left, right] 中在 t 中出现的字符的频数与 t 中的差值,如果等于 0 则说明某个字符的频数匹配上了。
// javascript
var minWindow = function(s, t) {
const cnt = new Map();
for (const tEle of t) cnt.set(tEle, (cnt.get(tEle) || 0) - 1);
const sLen = s.length;
let left = 0, ansL = -1, distance = sLen + 1;
let matchSuccess = 0;
for (let right = 0; right < sLen; right++) {
if (cnt.has(s[right]) === true) {
cnt.set(s[right], cnt.get(s[right]) + 1);
if (cnt.get(s[right]) === 0) {
matchSuccess += 1;
}
}
while (matchSuccess === cnt.size && left <= right) {
if (right - left + 1 < distance) {
distance = right - left + 1;
ansL = left;
}
if (cnt.has(s[left]) === true) {
cnt.set(s[left], cnt.get(s[left]) - 1);
if (cnt.get(s[left]) < 0) {
matchSuccess -= 1;
}
}
left++;
}
}
return ansL === -1 ? "" : s.substr(ansL, distance);
};