打卡LeetCode热题100(TS版本)
打卡第九天
Problem: 76. 最小覆盖子串
[TOC]
思路
最简单的方式是暴力解法,所谓的暴力解法是遍历出所有长度大于等于t字符串长度的子串,然后让每个子串和t串进行对比。对比方式还是比较特别的,可以采用hash方式来记录每个子串中的字符串,然后判断子串中的每一项是否都在hash中。 暴力破解的时间复杂度在3次方。执行后肯定会超时。由于暴力破解中含有很多重复的操作,我们可以简化。通过观察可以发现我们只需要先从左侧0索引开始先找出最短可以满足t串的子串。满足后我们再把这个子串从左侧开始从右侧缩进,也就是不断的抛弃左侧的元素,直到不能满足t串前停止。然后记录一下当前的子串的起始位置和终止位置。记录后还需要再往右滑动窗口,直到可以出现可以满足t串的子串。
解题方法
我们需要两个hash来记录t字符串中每个元素出现的次数,另外还需要记录在遍历s串时每个字符出现的次数。我们最终统计的s子串中所有出现在t中的字符串结果总和,也就是说如果出现 sMap(s[i]) < tMap(s[i]) 就需要记录一下当前出现在t中的个数总和:validChars。如果出现了 validChars === t.length 意味着该子串满足了t串的要求,并对该串进行缩进,缩进到刚好满足t串的要求,继续往右滑动窗口。
复杂度
- 时间复杂度:
设字符集是C,则时间复杂度是:
- 空间复杂度:
设字符集是C, 则空间复杂度是:
Code
function minWindow(s, t) {
const sLen = s.length;
const tLen = t.length;
if (sLen === 0 || tLen === 0 || sLen < tLen) {
return '';
}
const sMap = new Map();
const tMap = new Map();
for (let i = 0; i < tLen; i++) {
if (!tMap.has(t[i])) {
tMap.set(t[i], 0);
}
tMap.set(t[i], tMap.get(t[i]) + 1);
}
let minLen = Infinity; // Initialize to positive infinity
let begin = 0;
let left = 0;
let right = 0;
let validChars = 0; // Number of valid characters in the current window
while (right < sLen) {
const charR = s[right];
if (tMap.has(charR)) {
if (!sMap.has(charR)) {
sMap.set(charR, 0);
}
sMap.set(charR, sMap.get(charR) + 1);
if (sMap.get(charR) <= tMap.get(charR)) {
validChars++;
}
}
while (validChars === tLen) {
if (right - left < minLen) {
minLen = right - left;
begin = left;
}
const charL = s[left];
if (tMap.has(charL)) {
sMap.set(charL, sMap.get(charL) - 1);
if (sMap.get(charL) < tMap.get(charL)) {
validChars--;
}
}
left++;
}
right++;
}
if (minLen === Infinity) {
return '';
}
return s.slice(begin, begin + minLen + 1); // Include the end character in the result
}