最小覆盖子串
https://leetcode.cn/problems/minimum-window-substring/description/
问题描述
给定字符串 s 和 t,在 s 中找出包含 t 所有字符(包括重复个数)的最短连续子串,返回该子串。若不存在,返回空字符串。
解题思路
使用滑动窗口(双指针)算法,时间复杂度 O(m + n),空间复杂度 O(字符集大小)。
- 统计需求 :用哈希表
need记录t中每个字符需要的个数,并用required记录不同字符的种类数。 - 滑动窗口 :维护左右指针
l、r,初始时都指向 0。 - 扩展右指针 :每次移动
r扩大窗口,将当前字符加入窗口计数window。若该字符在need中且window[c] == need[c],则说明该字符的需求已满足,ans加 1。 - 收缩左指针 :当
ans == required时,说明当前窗口已覆盖t,尝试移动l缩小窗口,同时更新最小长度和起始位置。若移动后窗口不再满足,则ans减 1。 - 重复 :直到
r遍历完整个s。 - 结果:根据记录的最小起始位置和长度返回子串。
代码实现(C++)
cpp
class Solution {
public:
string minWindow(string s, string t) {
if(s.size()<t.size()) return "";
unordered_map<char,int> need,window;
for(char c:t) need[c]++;
int required =need.size();
int l = 0,r =0;
int ans=0;
int i=0,len=INT_MAX;
while(r<s.size()){
char c=s[r];
r++;
if(need.count(c)){//当前字符在t中
window[c]++;
if(window[c]==need[c]) ans++;
}
while(ans==required){//当前已满足子串包含t中的每一个字符
if(r-l<len){//如果当前子串更优
i=l;
len=r-l;
}
char d=s[l];
l++;
if(need.count(d)){
if(window[d]==need[d]) ans--;
window[d]--;
}
}
}
return len == INT_MAX ? "" : s.substr(i, len);
}
};
复杂度分析
- 时间复杂度:O(m + n),其中 m 和 n 分别为 s 和 t 的长度。每个字符最多被左右指针各访问一次。
- 空间复杂度:O(字符集大小),由于字符为英文字母,最多 52 个,可视为常数。若使用哈希表,则与字符种类数相关。
关键点
- 使用
need记录需求,window记录当前窗口的字符计数。 - 用
ans记录已满足需求的字符种类数,避免每次遍历整个need。 - 收缩窗口时,只有当
window[d] == need[d]时才减少ans,保证逻辑正确。