LeetCode 76题「最小覆盖子串」
LeetCode 76题「最小覆盖子串」是一道经典的滑动窗口算法题目,难度为困难。题目要求在给定的字符串 s
中找到包含字符串 t
所有字符的最小子串,若不存在则返回空字符串。
题目分析
输入:字符串 s
和 t
(均由英文字母组成)
输出:s
中包含 t
所有字符的最小子串,若不存在则返回空字符串
条件:
- 子串需包含
t
的所有字符(包括重复字符) - 若有多个符合条件的子串,返回长度最小的
- 时间复杂度尽量优化
解题思路
这道题可以使用滑动窗口算法结合哈希表来解决:
- 统计字符频率:使用哈希表统计
t
中每个字符的出现次数。 - 滑动窗口初始化:使用左右指针
left
和right
表示窗口边界,初始均指向s
的起始位置。 - 扩展窗口:右指针
right
向右移动,不断扩大窗口,直到窗口包含t
的所有字符。 - 收缩窗口:当窗口满足条件时,尝试收缩左指针
left
,尽可能缩小窗口大小,同时记录最小窗口的位置和长度。 - 重复步骤3-4:直到右指针遍历完整个字符串
s
。
算法实现
以下是使用C++实现的代码:
#include <string>
#include <unordered_map>
#include <climits>
using namespace std;string minWindow(string s, string t) {if (s.empty() || t.empty() || s.size() < t.size()) return "";// 统计t中各字符的出现次数unordered_map<char, int> target;for (char c : t) target[c]++;int required = target.size(); // 需要匹配的字符种类数// 滑动窗口初始化int left = 0, right = 0;int formed = 0; // 当前窗口中已完全匹配的字符种类数unordered_map<char, int> window; // 窗口中各字符的出现次数// 记录最小窗口的位置和长度int min_len = INT_MAX;int min_left = 0;while (right < s.size()) {// 扩展窗口,加入右侧字符char c = s[right];window[c]++;// 检查当前字符是否在t中,且窗口中的出现次数达到要求if (target.find(c) != target.end() && window[c] == target[c]) {formed++;}// 尝试收缩窗口while (left <= right && formed == required) {c = s[left];// 更新最小窗口if (right - left + 1 < min_len) {min_len = right - left + 1;min_left = left;}// 移出左侧字符window[c]--;if (target.find(c) != target.end() && window[c] < target[c]) {formed--;}left++;}right++;}return min_len == INT_MAX ? "" : s.substr(min_left, min_len);
}
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串
s
的长度。左右指针最多各遍历一次s
。 - 空间复杂度: O ( k ) O(k) O(k),其中 k k k 是字符串
t
中不同字符的个数。主要用于存储哈希表。
关键点解释
- 哈希表
target
:记录t
中每个字符的出现次数。 - 变量
required
:表示需要匹配的不同字符数(即target
的大小)。 - 变量
formed
:记录当前窗口中已完全匹配的不同字符数。 - 收缩窗口的条件:当
formed == required
时,表示当前窗口已包含t
的所有字符,此时尝试收缩窗口以找到最小子串。
通过这种方法,可以高效地在 s
中找到包含 t
所有字符的最小子串。