题目描述:
一个长度为 n 的字符串 s,其中仅包含 'Q', 'W', 'E', 'R' 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
思路:
假设字符串存储在字符数组S中,则用尺取法对每个连续序列分析即可。
两个问题:
1、为何当区间【L,R】不满足时,不尝试【L+1,R】,而是尝试【L,R+1】? 答:因为【L+1,R】是【L,R】的一个子状态,如果【L,R】都不符合条件,则【L+1,R】更不可能符合条件。具体的说:在区间【L,R】的所有变换中,取 S[L] 不变的变换,这些变换也就是在区间【L+1,R】中的所有变换。
2、对满足条件的判断,我是用了PPT中的方法,算FREE是不是4的倍数,后来发现Shared的代码中有一个更简洁的,仔细分析了一下,过程如下:
所以只要MAX<=n/4即可。
ADD函数:
最初写的代码比较繁琐,后来借鉴了用户“karles”的shared的代码,发现其定义了一个add函数,非常好用,把问题抽象的特别到位。
最初统计时,传1给val;区间移动时,传-1/1给val;
1 void add(char c,int val) 2 { 3 if(c=='Q') x+=val; 4 else if(c=='W') y+=val; 5 else if(c=='E') z+=val; 6 else if(c=='R') w+=val; 7 }
最后版本代码:
1 #include <cstdio> 2 #include <iostream> 3 #include <climits> 4 #include <cstring> 5 using namespace std; 6 const int MAXN=1e6+5; 7 char s[MAXN]; 8 int x,y,z,w; 9 void add(char c,int val) 10 { 11 if(c=='Q') x+=val; 12 else if(c=='W') y+=val; 13 else if(c=='E') z+=val; 14 else if(c=='R') w+=val; 15 } 16 int main() 17 { 18 scanf("%s",s+1); 19 int n=strlen(s+1); 20 for(int i=1;i<=n;i++) 21 add(s[i],1); 22 if(max(max(x,y),max(z,w))==n/4) 23 { 24 cout<<0<<endl; 25 return 0; 26 } 27 int l=1,r=1,ans=INT_MAX; 28 add(s[1],-1); 29 while(r<=n) 30 { 31 if(max(max(x,y),max(z,w))<=n/4) 32 { 33 ans=min(ans,r-l+1); 34 if(l==r) break; 35 else add(s[l],+1),l++; //注意xyzw是除掉LR区间的 36 } 37 else r++,add(s[r],-1); 38 } 39 cout<<ans<<endl; 40 return 0; 41 }