携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情
最小覆盖子串
给你一个字符串
s
、一个字符串t
。返回s
中涵盖t
所有字符的最小子串。如果s
中不存在涵盖t
所有字符的子串,则返回空字符串""
。
分析
- 根据题意可知,要得到s覆盖t的最小子串,在遍历s的时候维护一个区间,这个区间里有t中的所有字母,用一个变量维护这个区间的长度,长度最小的那个区间对应的子串就是输出的答案
- 对于这个变化的区间,能比较容易的想到滑动窗口,这次的滑动窗口是长度可变的窗口,配合双指针指向窗口的首尾
- 在s中有t的字母时,记录匹配到的字母的个数,当这个个数等于t的长度的时候,此时的窗口是能覆盖t的,当出现这种情况时,可以通过移动滑动窗口的左指针来减小长度,同时需要维持覆盖t的条件,记录符合的子串,当不符合覆盖t的条件时,滑动窗口的右指针继续遍历,如此循环往复直至遍历结束
- 如何在s中匹配t,可以通过对象或者Map,通过
字符-字符出现的次数
这样的pair去管理t,在管理t的字母这里有个陷阱,即t有重复字符的情况,所以要处理重复字符的情况,需要记录字符种类的个数,在匹配的时候也是记录匹配到的字符种类的个数
代码
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let map = {}, uniqueChars = 0;
for (let char of t) {
if (char in map) {
map[char] += 1;
} else {
map[char] = 1;
uniqueChars += 1;
}
}
let ans = '';
let left = 0, match = 0;
for (let right = 0; right < s.length; right++) {
let rightChar = s[right];
if (rightChar in map) {
map[rightChar] -= 1;
if (map[rightChar] === 0) match += 1;
}
if (match === uniqueChars) {
while (match === uniqueChars) {
let leftChar = s[left++];
if (map[leftChar] === 0) match -= 1;
map[leftChar] += 1;
}
let solution = s.slice(left-1, right+1);
ans = (ans === '')? solution: (ans.length > solution.length)? solution: ans;
}
}
return ans;
}
总结
- 这道题目扩展了滑动窗口的思路,滑动窗口长度也可以是变化的,在匹配字符串的时候,也能总结出一套思路,使用Map管理被匹配的字符,在遍历的时候通过对Map的value的操作从而确定是否匹配成功
- 今天也是有收获的一天