解答
基本的滑动窗口应该不难想到,关键是如何判断s的滑动窗口中包含t的所有字母。
代码一
对s的滑动窗口和字符串t统计字母频率,记录在scnt和tcnt中,对于每个位置i的字母, scnt[i] ≥ \ge ≥ tcnt[i], 否则s的滑动窗口不能完全包含字符串t。由此得到函数judge
class Solution {
public:
bool judge(int* scnt, int* tcnt, string t){
for(int i=0; i<t.size(); i++){
if(scnt[ t[i] ] < tcnt[ t[i] ]) return false;
}
return true;
}
string minWindow(string s, string t) {
int left=0, ans=s.size()+1, start=0, len=0;
int scnt[128] = {0}, tcnt[128] = {0};
for(int i=0; i<t.size(); i++){
tcnt[ t[i] ]++;
}
for(int i=0; i<s.size(); i++){
scnt[ s[i] ]++;
if(tcnt[ s[i] ] == 0) continue;
while(judge(scnt, tcnt, t)){
len = i-left+1;
if(ans > len){
ans = len;
start=left;
}
scnt[ s[left++] ]--;
}
}
return ans==s.size()+1 ? s.substr(start, ans) : "";
}
};
上面的代码思路很清晰,可惜TLE
在后面写完代码二后,回头再看代码一,查看265测试用例,发现字符串t是很长的,中间judge函数的复杂度为O(t.size()),需要对judge进行优化。
其实本来对judge优化没有太大的把握,只是尽量减少265测试用例的复杂度,将原来需要循环t.size()次减少为26*2次(有的样例中同时有大小写),只判断ASCII中英文字母
bool judge(int* scnt, int* tcnt, string t){
for(int i='A'; i<='Z'; i++){
if(scnt[ i ] < tcnt[ i ]) return false;
}
for(int i='a'; i<='z'; i++){
if(scnt[ i ] < tcnt[ i ]) return false;
}
return true;
}
勉强通过
代码二:
同样是滑动窗口的思想,同样是对judge函数进行优化。将judge过程从O(t.size())降低到了O(1)的复杂度。
在每一次滑动窗口移动的过程中,judge函数都对scnt和tcnt整个进行判断,但是每一次滑动窗口移动的过程中大部分元素没有变,每次只变化了一个
右边界向右滑动时
- 要么winFreq[s[right]] >= tFreq[s[right]], 此时s[right]在字符串t中没有出现(tFreq[s[right]]=0) 或是s[right]在s中已经够了(winFreq[s[right]] >= tFreq[s[right]]),
- 要么winFreq[s[right]] < tFreq[s[right]],s[right]在s中不够
核心部分是dis==t.size(),相当于judge函数,表示滑动窗口内部包含了t中的所有字符
class Solution {
public:
string minWindow(string s, string t){
int left=0, start=0, len=0, ans=s.size()+1;
int scnt[128]={0}, tcnt[128]={0};
for(int i=0; i<t.size(); i++)
tcnt[ t[i] ]++;
// dis表示滑动窗口内部包含了t中字符的个数
int dis=0;
for(int i=0; i<s.size(); i++){
// s[i]不在t中
if( tcnt[ s[i] ] == 0 ) continue;
// 否则s[i]是t中的字符, 且s[i]在滑动窗口中不够,dis++
if( scnt[ s[i] ]++ < tcnt[ s[i] ] ) dis++;
// 滑动窗口中已经全部包含了t的字母(dis==t.size())
while( dis == t.size() ){
len = i-left+1;
if(ans > len){
ans = len;
start = left;
}
// 左边界的字母是t中的字母,left要向右移动,该元素被丢掉
if( tcnt[s[left]] != 0 ){
if( scnt[s[left]]-- <= tcnt[s[left]] ) dis--;
}
left++;
}
}
return ans==s.size()+1 ? "" : s.substr(start, ans);
}
};
类似题目
438 找到字符串中所有字母异位词(中等)
这个题目的滑动窗口大小固定,同样使用了dis的概念