题意:
一个长度为n(n是4的倍数)的字符串s,其中仅包含’Q’,‘W’,‘E’,'R’四种字符。若四种字符在字符串中出现次数均为n/4,则其为一个平衡字符串。现可以将s中连续的一段子串替换为相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?若s已经平衡则输出0。
思路:
因为所求解答案为一个连续区间,区间左右端点移动有明确方向,可以使用尺取法:若当前区间[l,r]可以通过替换使得字符串平衡,则l++(l==r时l++,r++);若[l,r]不可以使字符串平衡,则r++。
接下来要判断对于给定的[l,r],能否通过替换使得字符串平衡。思路为通过替换使得4类字符数量一致,再判断剩余空闲位置是否是4的倍数。首先用sum1~sum4记录字符串里QWER的个数,用sum11~sum44记录[l,r]内的QWER个数。记max是sum11~sum44中最大的,则max-sum11+max-sum22+max-sum33+max-sum44是[l,r]区间应给区间外的字符串补充的字符个数。[l,r]字符总数total=r-l+1,减去要补充的字符数,还剩下free个空闲位置。若free>=0且为4的倍数,则可以通过替换使得字符串平衡。
总结:
一道关于尺取法的题目,判断区间是否满足条件的思路很是巧妙。同时应注意要特判字符串已经平衡的情况,否则代码会判断将长度为1的区间进行替换可以满足条件,从而输出1而不是0。
代码:
#include <iostream>
using namespace std;
char s[100000];
int sum1,sum2,sum3,sum4;
int ans=100000;
int main()
{
cin>>s;
sum1=0,sum2=0,sum3=0,sum4=0;
int index=0;
while(s[index]!='\0')
{
if(s[index]=='Q') sum1++;
else if(s[index]=='W') sum2++;
else if(s[index]=='E') sum3++;
else if(s[index]=='R') sum4++;
index++;
}
if(sum1==sum2&&sum2==sum3&&sum3==sum4) //特判,当前已经平衡
{
cout<<0<<endl;
return 0;
}
//[0,index-1]有字符,共index个
int l=0,r=0;
while(r<=index-1)
{
//计算[l,r]的QWER个数
int sum11=0,sum22=0,sum33=0,sum44=0;
for(int i=l; i<=r; i++)
{
if(s[i]=='Q') sum11++;
else if(s[i]=='W') sum22++;
else if(s[i]=='E') sum33++;
else if(s[i]=='R') sum44++;
}
sum11=sum1-sum11;
sum22=sum2-sum22;
sum33=sum3-sum33;
sum44=sum4-sum44;
int max=0;
if(sum11>max) max=sum11;
if(sum22>max) max=sum22;
if(sum33>max) max=sum33;
if(sum44>max) max=sum44;
int total=r-l+1;
int free=total-((max-sum11)+(max-sum22)+(max-sum33)+(max-sum44));
if(free>=0&&free%4==0) //满足要求
{
if(total<ans) ans=total;
if(l==r) l++,r++;
else l++;
}
else r++;
}
cout<<ans<<endl;
}