本题使用滑动窗口解决,用right表示滑动窗口的右边界,left表示滑动窗口的左边界。寻找可行解,我们可以这样约定滑动窗口的意义:right指针向右移动,是使得滑动窗口找到可行解。left指针向右移动是为了更新窗口使得其可以继续寻找下一个可行解。那么本题中滑动窗口的right指针移动是为了使得窗口内包含t的所有字符,而当已经包含了所有字符时,这就是一个可行解,我们移动left指针使之“不可行”,寻找下一个可行解。
我们要定义一个集合来表示是否包含了t,由于本题是包含t 多于t是没关系的,我们可以定义一个vector用来记录数量,每个字符多于了t的数量才算对。我们当然也可以用map来记录数量,每个字符多于了t的数量才算对。但是用vector必须记录52个字符,而用map比较时跟t的字符数量有关。
本题也容易想到的是,既然s中只需要考察包含t的所有字符,那我们可以把s中的不在t中的字符都去掉,然后记录去掉之后的new_s的各个字符在s中的位置,这样我们只需要在new_s中寻找一个区间包含t即可。这样做会比不去掉快吗?
去掉之后少了一次判断字符是否在t中(去掉时:预处理时扫描一次;不去掉时:left和right各扫描一次,所以只少了一次)
滑动窗口 vector:
保证左指针始终指向t中的元素,即让左指针指向的元素一定构成可行解的一部分。
class Solution {
public:
bool cmp(vector<int> &s,vector<int> & t){//true表示全都包含了
int i=0;
while(i<s.size()){
if(s[i]<t[i]) return false;
++i;
}
return true;
}
string minWindow(string &s, string &t) {
int len=INT_MAX,ansl=0,ansr=-1;
string ans="";
vector<int> cnt_t(52),cnt_s(52);
unordered_map<char,int> Getval;
for(char ch='a';ch<='z';++ch) {Getval[ch]=ch-'a';Getval[ch-'a'+'A']=ch-'a'+26;}
for(auto &i:t) cnt_t[Getval[i]]+=1;
int left=0,right=-1,s_len=s.size();
while(right<s_len){
while(left<s_len&&!cnt_t[Getval[s[left]]]) ++left;//left始终指向t中的元素
if(left==s_len) break;
while(right<s_len-1&&!cmp(cnt_s,cnt_t)){
++right;
if(cnt_t[Getval[s[right]]]) cnt_s[Getval[s[right]]]+=1;
}
if(right-left<len&&cmp(cnt_s,cnt_t)){
len=right-left;
ansl=left;
ansr=right;
}//窗口内包含所有t的字符
cnt_s[Getval[s[left++]]]-=1;
}
if(ansl<=ansr) ans=s.substr(ansl,len+1);
return ans;
}
};