Week5 作业 C - 平衡字符串 Gym - 270737B 尺取法

题目

一个长度为 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; //完成处理,输出结果

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值