竟然有一个月没刷题了。。放完假咯,可以继续学习了~
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
解法1 暴力解法
思路
首先遍历 s 的每一个起始位置 i。从 i 开始扩大子串,直到 j。对于每个子串 s[i:j] 都检查是否包含字符串 t 的所有字符,如果存在,则更新最短的长度和字符串。
代码
function minWindow(s: string, t: string): string {
if (!s.length || !t.length) {
return "";
}
const containAllChars = (substr, tFreq):boolean => {
const subStrFreq: { [key: string]: number } = {};
for (const char of substr) {
subStrFreq[char] = (subStrFreq[char] || 0) + 1;
}
for (const char in tFreq) {
if (!subStrFreq[char] || subStrFreq[char] < tFreq[char]) {
return false;
}
}
return true;
}
const tFreq: { [key: string]: number } = {};
for (const char of t) {
tFreq[char] = (tFreq[char] || 0) + 1;
}
let minLen: number = Infinity;
let minSubStr: string = "";
for (let i = 0; i < s.length; i++) {
for (let j = i + 1; j <= s.length; j++) {
const subStr = s.substring(i, j);
if (containAllChars(subStr, tFreq)) {
if (subStr.length < minLen) {
minLen = subStr.length;
minSubStr = subStr;
}
}
}
}
return minSubStr;
};
时空复杂度分析
时间复杂度:两层遍历枚举 i, j 时是O(n^2),加上检查该字符串所需要时间,所以是 O(n^3)
空间复杂度:O(n),用于存储子串的字符频率表
解法2 滑动窗口
代码
function minWindow(s: string, t: string): string {
if (!s.length || !t.length) {
return "";
}
const tFreq: { [key: string]: number } = {};
for (const char of t) {
tFreq[char] = (tFreq[char] || 0) + 1;
}
// 左右指针、已匹配的字符数、窗口内字符频率
let left = 0, right = 0;
let required = Object.keys(tFreq).length;
let formed = 0;
const windowCounts: { [key: string]: number } = {};
// 用于记录最小窗口的起始位置和长度
let minLen = Infinity;
let minLeft = 0;
while (right < s.length) {
const char = s[right];
windowCounts[char] = (windowCounts[char] || 0) + 1;
// 如果当前字符的频率满足 t 中的要求,增加 formed 计数
if (tFreq[char] && windowCounts[char] === tFreq[char]) {
formed++;
}
// 尝试缩小窗口
while (left <= right && formed === required) {
const currentChar = s[left];
// 更新最小子串
if (right - left + 1 < minLen) {
minLen = right - left + 1;
minLeft = left;
}
// 移除窗口最左侧的字符,并更新频率
windowCounts[currentChar]--;
if (tFreq[currentChar] && windowCounts[currentChar] < tFreq[currentChar]) {
formed--;
}
left++;
}
// 继续扩大窗口
right++;
}
return minLen === Infinity ? "" : s.substring(minLeft, minLeft + minLen);
};
时空复杂度
时间复杂度:O(|t| + |s|) , |t| 代表 t 的长度
空间复杂度:O(|t| + |s|)