最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

1
2
3
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

1
2
3
输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

1
2
3
4
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

提示:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • st 由英文字母组成

进阶:你能设计一个在 o(m+n) 时间内解决此问题的算法吗?


跟上一个题类似。很容易想到用哈希表和滑动窗口。

  • 建立哈希表ht记录t中字母的出现频次。
  • s上维护一个滑动窗口,并建立哈希表hs记录窗口中字母的出现频次。
  • 窗口内每个单词的频次都大于等于t中频次时,记录下当前窗口起点和长度,并尝试移动窗口左边界直到不满足条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char,int> hs,ht;
for(auto i:t) ht[i]++;
int cnt=ht.size(); //记录有几种字母
int start=0,len=INT_MAX;
int k=0;
for(int left=0,right=0;right<s.size();right++){
int r=s[right];
hs[r]++;
if(hs[r]==ht[r]) k++; //如果窗口内的该字母符合要求,k++
while(k==cnt){
if(right-left+1<len){
start=left;
len=right-left+1;
}
int l=s[left]; //左指针移动
hs[l]--;
left++;
if(hs[l]<ht[l]) k--;
}
}
return len==INT_MAX?"":s.substr(start,len);
}
};

这个解法用了两个哈希表,时间和空间都比较高,可以优化成一个哈希表。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char,int> ht;
for(auto i:t) ht[i]++;
int cnt=t.size(); //cnt记录字母总数
int start=0,len=INT_MAX;
for(int left=0,right=0;right<s.size();right++){
int r=s[right];
if(ht[r]>0) cnt--;
ht[r]--;
while(cnt==0){
if(right-left+1<len){
start=left;
len=right-left+1;
}
int l=s[left];
ht[l]++;
if(ht[l]>0) cnt++;
left++;
}
}
return len==INT_MAX?"":s.substr(start,len);
}
};

时间和空间都提高了不少。但还是不够快。因为字母都是大小写字母可以把哈希表优化成数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
string minWindow(string s, string t) {
vector<int> ht(58,0);
for(auto i:t) ht[i-'A']++;
int cnt=t.size();
int start=0,len=INT_MAX;
for(int left=0,right=0;right<s.size();right++){
int r=s[right];
if(ht[r-'A']>0) cnt--;
ht[r-'A']--;
while(cnt==0){
if(right-left+1<len){
start=left;
len=right-left+1;
}
int l=s[left];
ht[l-'A']++;
if(ht[l-'A']>0) cnt++;
left++;
}
}
return len==INT_MAX?"":s.substr(start,len);
}
};