最小覆盖子串
题目链接
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
示例 2:
输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
m == s.length
n == t.length
1 <= m, n <= 105
s 和 t 由英文字母组成
进阶:你能设计一个在 o(m+n) 时间内解决此问题的算法吗?
解题思路
- 滑动窗口,双指针
- 可以使用
unordered_map<char,int>mt
统计字符串s的字符个数 - 然后设定两个指针
l
和r
,以及unordered_map<char,int>ms
维护l到r之间的字符数量 - 如果
ms
符合要求,那么l
指针向右移动,缩小窗口;如果ms
不符合要求,那么r
指针向右移动,扩张窗口; - 此外有两个优化点需要注意:
- 指针移动过程中可以剔除不在t中的字符;
- 指针移动过程中可以剔除
ms
中数量比mt
多的冗余字符;
解题代码
class Solution {
public:
unordered_map<char,int>ms, mt;
string minWindow(string s, string t) {
for(int i=0;i<t.length();i++){
mt[t[i]]++;
}
string ans="";
int max_len=1e5+5;
int l=0,r=0;
ms[s[r]]++;
while(l<=r&&r<s.length()){
bool ok=true;
if(ms.size()>=mt.size()){
unordered_map<char,int>::iterator iter = mt.begin();
for(;iter!=mt.end();iter++){
if(ms[iter->first]<iter->second){
ok=false;
break;
}
}
}
else ok=false;
if(ok){
if(max_len>r-l+1){
ans = s.substr(l,r-l+1);
max_len = r-l+1;
}
if(ms[s[l]]>0)ms[s[l]]--;
else ms[s[l]]=0;
l++;
while(l<=r&&mt.count(s[l])==0)l++;
while(l<=r&&ms[s[l]]>mt[s[l]])ms[s[l++]]--;
}
else{
r++;
if(r<s.length())ms[s[r]]++;
}
}
return ans;
}
};