问题描述
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Sample input && output
Sample input1
QWER
Sample output1
0
Sample input2
QQWE
Sample output2
1
Sample input3
QQQE
Sample output3
2
Sample input4
QQQQ
Sample output4
3
Note
1
<
=
n
<
=
1
0
5
1<=n<= 10^5
1<=n<=105
n是4的倍数
字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.
解题思路
假设要更改的区间是 [ l , r ] [l,r] [l,r],这是一个连续区间,并且对于区间的左右端点有着一个明确方向:搜索的时候从左向右搜索即可。因此,可以使用尺取法。
尺取法,即双指针法。就是维护一个区间的左端点和右端点,两个端点向着相同的方向扫描。我在这里根据这个题目来讲解尺取法的原理。
首先,当区间 [ l , r ] [l,r] [l,r]满足条件时,即替换 [ l , r ] [l,r] [l,r]中的元素,可以使字符串变成平衡字符串的时候,我们应该向右移动哪个端点?
如果移动右端点的话,区间长度变长,并且原始区间 [ l , r ] [l,r] [l,r]符合要求,那么 [ l , r + 1 ] [l,r+1] [l,r+1]必定也符合要求(可以想成 r + 1 r+1 r+1这个位置不替换)。
如果移动左端点,区间长度变小,新的区间少了 l l l那个位置,有可能符合要求,也有可能不符合要求,如果符合要求,我们的答案就取新的长度,这是对答案有贡献的。
再看看当区间 [ l , r ] [l,r] [l,r]不符合条件的时候,自然是要 r + + r++ r++,因为 [ l , r ] [l,r] [l,r]都不符合条件了,比它更短的 [ l + 1 , r ] [l+1,r] [l+1,r]必定不符合条件。
注意当 l = = r l==r l==r的时候,我们将 l + + , r + + l++,r++ l++,r++。
上面这些就是尺取法的精髓,那么对于这道题而言,我们如何判断区间 [ l , r ] [l,r] [l,r]是否符合要求呢?
- 用sumq, sumw, sume, sumr来记录不包含区间 [ l , r ] [l,r] [l,r]时,字符’Q’, ‘W’,‘E’,'R’的个数 。
- 先通过替换,使得4类字符的数量一致,然后判断剩余的空闲位置是否是4的倍数。
第1步对于不含区间 [ l , r ] [l,r] [l,r]时,字符’Q’, ‘W’,‘E’,'R’的个数的计算,每当区间移动的时候,我们都可以从上一个状态得到这一个状态的值,即当 l + + l++ l++时,可以调用adjust_inc()函数,判断刚刚移动出区间的 l 原 l_{\text{原}} l原时什么字符,然后加到sum中。 r + + r++ r++同理,调用adjust_dec()函数,从四个sum中减去 r 原 r_{\text{原}} r原的字符。两种函数如下:
void adjust_dec(int x)
{
if(s[x]=='Q') sumq--;
else if(s[x]=='W') sumw--;
else if(s[x]=='E') sume--;
else if(s[x]=='R') sumr--;
}
void adjust_inc(int x)
{
if(s[x]=='Q') sumq++;
else if(s[x]=='W') sumw++;
else if(s[x]=='E') sume++;
else if(s[x]=='R') sumr++;
}
第2步计算方法代码如下:
bool check()//判断是否满足要求
{
int maxx=max(sumq,max(sumw,max(sume,sumr)));
int total=r-l+1;
int free=total-((maxx-sumq)+(maxx-sume)+(maxx-sumr)+(maxx-sumw));
if(free%4==0 && free>=0)//满足要求
{
ans=min(ans,total);
return true;
}
return false;
}
完整代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
string s;
int ans=INT_MAX,sumq,sumw,sume,sumr,l,r;
bool check()//判断是否满足要求
{
int maxx=max(sumq,max(sumw,max(sume,sumr)));
int total=r-l+1;
int free=total-((maxx-sumq)+(maxx-sume)+(maxx-sumr)+(maxx-sumw));
if(free%4==0 && free>=0)
{
ans=min(ans,total);
return true;
}
return false;
}
void adjust_dec(int x)
{
if(s[x]=='Q') sumq--;
else if(s[x]=='W') sumw--;
else if(s[x]=='E') sume--;
else if(s[x]=='R') sumr--;
}
void adjust_inc(int x)
{
if(s[x]=='Q') sumq++;
else if(s[x]=='W') sumw++;
else if(s[x]=='E') sume++;
else if(s[x]=='R') sumr++;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>s;
for (int i=0; i<s.size(); i++)
{
adjust_inc(i);
}
if(sumq==s.size()/4 && sume==s.size()/4 && sumr==s.size()/4 && sumw==s.size()/4)
{
cout<<0<<endl;
return 0;
}
adjust_dec(0);//从s[0]开始,所以先处理一下s[0]
while(r<s.size())
{
if(check()){
adjust_inc(l);
if(l==r) adjust_dec(++r);
l++;
}
else adjust_dec(++r);
}
cout<<ans<<endl;
return 0;
}