class Solution {
public:
string minWindow(string s, string t) {
int n1=s.size(),n2=t.size();
string ans;
if(n1<n2)
return ans;
int ct1[130]={0},ct2[130]={0},ct3[130]={0};
int oks=0;
for(int i=0;i<n2;++i)
{
if(ct2[t[i]]==0) ++oks; //统计有几类字符
++ct2[t[i]];
}
int l=0,r=0,ok=0,i;
int flag=1,minN=100010,temp,pos;
char tc;
while(r<n1&&l<=r)
{
tc=s[r];
++ct1[tc];
if(ct2[tc]&&!ct3[tc]&&ct1[tc]==ct2[tc]) //对t中存在的字符进行比较,若相等则ok数+1
{
++ok;
ct3[tc]=1; //重复则不计数
}
if(ok==oks)
{
if(!flag)
flag=1;
tc=s[l];
while(ct1[tc]>ct2[tc]&&l<=r) //找到s和t第一个相等个数字符的位置
{
--ct1[tc];
++l;
tc=s[l];
}
temp=r-l+1;
if(temp<minN) //更新长度
{
minN=temp;
pos=l;
}
--ct1[tc];
--ok;
ct3[tc]=0;
++l; //计数少1,清除重复标记,左指针向右移动
}
++r; //右指针移动,滑动窗口向右扩展
}
if(flag) //存在结果则更新ans
ans=s.substr(pos,minN);
return ans;
}
};
由于是求最短子串,很容易联想到使用滑动窗口去解决问题。可以按寻找合法子串->缩短子串->滑动窗口移动的思路进行解题。
(1)寻找合法子串:不使用哈希表,map等数据结构,而是使用三个计数数组。ct1统计s中字符出现次数,ct2统计t中字符出现次数,ct3标记“有效的包含”,其对单个字符进行统计,oks指t中出现的字符种类个数,ok是s中合法字符的种类个数。当s的子串s'有oks个合法字符,且每种字符的数量大于等于t中各字符的数量,则视为合法子串,接下来对该子串进行缩短。
(2)缩短子串:此时移动左指针,过滤掉s中从左指针处开始多于t中的字符,找到第一个字符数相等的位置,易知此时为最短子串位置。注意l要小于等于r这一条件,否则会越界。
(3)滑动窗口移动:ok数减1,清除ct3数组中的统计标志,ct1[s[l]]字符的计数减1,左右指针l和r同时自加,直到遍历完成。
变量名不太美观,如果可以的话跟着代码模拟一遍,注意括号内条件。