题目
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input
一行字符表示给定的字符串s
Output
一个整数表示答案
Examples
Input
QWER
Output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
Note
1<=n<=10^5
n是4的倍数
字符串中仅包含字符 'Q', 'W', 'E' 和 'R'.
解题思路
所求解的答案为一个连续区间,区间左右端点移动有明确方向,我们可以用尺取法解。当区间满足要求时,缩小区间,左端点右移;当区间不满足条件时,右端点右移,扩大区间。
首先统计输入字符串中 Q W E R 出现的个数,我们计选中区间外 Q W E R 字符出现的次数分别是 s1 s2 s3 s4 ,如果这个字符是平衡字符串,那么就满足 s1+s2+s3+s4==max(s1,s2,s3,s4),不用处理字符串就是平衡字符串,程序输出0。
程序用 l r 作为选取的左端点和右端点,同时还维护 lastl 和 lastr ,分别存储统计 s1-s4 时左右端点的位置。初始 l=r=0,运行着上面的选取方法,当区间满足要求时即 checksum(l, r) 返回值为真,缩小区间,左端点右移;当区间不满足条件时,右端点右移,扩大区间。
checksum(l, r) 函数判断当前选取是否满足条件,满足条件则返回真。具体地,比较当前选择的区间和上次选择的区间,比较差异,改变 Q W E R 的统计值,判断当前统计值是否满足平衡字符串的要求,即区间外的固定的字符串和区间内任选的字符串能组成平衡字符串, Q W E R 出现的次数相等。此时需要满足是式子为 (r - l + 1) - ( max(s1,s2,s3,s4) - (s1+s2+s3+s4) ) 大于等于 0 且为 4 的倍数。
最后,选取满足上述条件最小的选取范围并输出。
程序源码
#include<iostream>
using namespace std;
string a;
int s1 = 0, s2 = 0, s3 = 0, s4 = 0, max1 = 0; //当前剩余字符统计
int lastl = 0, lastr = 0; //
int initial(int size) { //初始化
s1 = 0, s2 = 0, s3 = 0, s4 = 0; //统计清零
for (int i = 0; i < size; i++) { //统计字符出现次数
if (a[i] == 'Q') s1++;
else if (a[i] == 'W') s2++;
else if (a[i] == 'E') s3++;
else if (a[i] == 'R') s4++;
}
int max1 = s1; //取最大值
if (s2 > max1) max1 = s2;
if (s3 > max1) max1 = s3;
if (s4 > max1) max1 = s4;
if (-max1 - max1 - max1 - max1 + s1 + s2 + s3 + s4 == 0) { //输入的字符串平衡
return 1; //返回真
}
if (a[0] == 'Q') s1--; //尺取第一个字符,统计减去第一个字符
else if (a[0] == 'W') s2--;
else if (a[0] == 'E') s3--;
else if (a[0] == 'R') s4--;
return 0;
}
int checksum(const int l, const int r) { //校验当前尺取情况是否符合条件
if (l != lastl) { //当前左端点和上次不一致,说明左端点发生移动,更改统计值
if (a[lastl] == 'Q') s1++;
else if (a[lastl] == 'W') s2++;
else if (a[lastl] == 'E') s3++;
else if (a[lastl] == 'R') s4++;
}
else if (r != lastr) { //当前右端点和上次不一致,说明右端点发生移动,更改统计值
if (a[r] == 'Q') s1--;
else if (a[r] == 'W') s2--;
else if (a[r] == 'E') s3--;
else if (a[r] == 'R') s4--;
}
lastl = l; lastr = r; //保存选取的端点值
max1 = s1;
if (s2 > max1) max1 = s2; //判断剩余的自由点是否大于等于0且为4的倍数
if (s3 > max1) max1 = s3;
if (s4 > max1) max1 = s4;
int count = r - l + 1 - max1 - max1 - max1 - max1 + s1 + s2 + s3 + s4;
if (count >= 0 && count % 4 == 0) return 1; //符合条件,返回真
return 0;
}
int main() {
cin >> a; //获取字符串
int size = a.size();
int left = 0, right = 0;
int result = size + 1;
if (initial(size)) { //初始化
cout << 0 << endl;
return 0;
}
while (right <= size) { //尺取法
if (checksum(left, right)) { //满足条件
if (result > right - left + 1) {
result = right - left + 1; //该解更优,更新结果
}
if (left < right) { //左端点右移
left++;
}
else right++; //左右端点重合,右端点右移
}
else {
right++; //不满足条件,右端点右移
}
}
cout << result << endl; //完成处理,输出结果
}