本题使用滑动窗口解决,用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;}
};