WEEK5 周记 作业——尺取法&前缀和_平衡字符串

WEEK5 周记 作业——尺取法&前缀和_平衡字符串

一、题意

1.简述

一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。

如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。

现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?

如果 s 已经平衡则输出0。

2.输入格式

一行字符表示给定的字符串s

3.输出格式

一个整数表示答案

4.样例

Input_1

QWER

Output_1

0

Input_2

QQWE

Output_2

1

Input_3

QQQW

Output_3

2

Input_4

QQQQ

Output_4

3

样例解释

1<=n<=10^5

n是4的倍数

字符串中仅包含字符 ‘Q’, ‘W’, ‘E’ 和 ‘R’.

二、算法

主要思路

考虑尺取法。尺取法就是前后两个指针框出字符串的一个范围,然后检查这个范围内的字符串是否满足要求,若不满足要求,将右指针右移,扩大范围,直到满足要求。需要注意的是,尺取法有一个特点,就是大范围不满足,那么这个范围中的小范围也必定不满足。针对这个题来说,长串不满足要求,不可能长串中的短串满足要求,因为只要将长片段中的多出来的部分原样保留就相当于又在处理短片段,就一定满足要求了。当框出一个满足要求的范围,再将左指针右移,直到不满足要求。最后一次移动前的范围就是最小的范围。


该题计算一个字符串中各个字符的数量,可以采用前缀和数组的方法,前缀和数组可以在O(1)的时间求出一个区域内所有元素数值之和。
前缀和数组: s u m [ i ] = s u m [ i − 1 ] + a [ i ] sum[i]=sum[i-1]+a[i] sum[i]=sum[i1]+a[i] s u m [ L , R ] = s u m [ R ] − s u m [ L − 1 ] sum[L,R]=sum[R]-sum[L-1] sum[L,R]=sum[R]sum[L1]
这个题可以针对每一个字符设置一个前缀和数组。


还可以用二分的方法。二分就是先检查区间 [ 1 , n ] [1,n] [1n],然后检查 [ 1 , n / 2 ] [1,n/2] [1,n/2] [ n / 2 , n ] [n/2,n] [n/2,n],然而再在每个区间上继续二分……如果发现一个区间不满足,就停止在这个区间上继续二分。记录下停止之前最小的满足要求的区间大小,然后取这些里面最小的。


三、代码

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
char str[100010];
int q[100010];
int w[100010];
int e[100010];
int r[100010];
int n;
//长的一定比短的更可能满足要求,不可能长片段中的短片段满足要求
//但是长片段却不满足要求,因为只要将长片段中的多出来的部分原样保留 
//就相当于又在处理短片段,就一定满足要求了
//所以这个题也可以用二分的方法
//不过由于不用二分也能做,所以这里没有选择二分
//二分就是1,n然后1,n/2……,找到满足的了,就n/4,n/2,然后3n/8,n/2…… 
bool check(int i,int j)
{
	int sumq=q[i-1]+q[n]-q[j];
	int sumw=w[i-1]+w[n]-w[j];
	int sume=e[i-1]+e[n]-e[j];
	int sumr=r[i-1]+r[n]-r[j];
	int suma=sumq>sumw?sumq:sumw;
	int sumb=sume>sumr?sume:sumr;
	int msum=suma>sumb?suma:sumb;
	int d=4*msum-sumq-sumw-sume-sumr;
	if(d<=j-i+1) 
		return (j-i+1-d)%4==0?true:false;
	else 
		return false;
} 
int main()
{
	string s;
	cin>>s;
	strcpy(str,s.c_str());
	n=s.length();
	int min=n;
	int i,j;
	for(i=1;i<=n;i++)
	{
		if(str[i-1]=='Q') 
		{
			q[i]=q[i-1]+1;
			w[i]=w[i-1];
			e[i]=e[i-1];
			r[i]=r[i-1];
		}
		else if(str[i-1]=='W')
		{
			w[i]=w[i-1]+1;
			q[i]=q[i-1];
			e[i]=e[i-1];
			r[i]=r[i-1];
		} 
		else if(str[i-1]=='E')
		{
			e[i]=e[i-1]+1;
			w[i]=w[i-1];
			q[i]=q[i-1];
			r[i]=r[i-1];
		}
		else if(str[i-1]=='R')
		{
			r[i]=r[i-1]+1;
			w[i]=w[i-1];
			e[i]=e[i-1];
			q[i]=q[i-1];
		}
	}
	if(q[n]==w[n]&&w[n]==e[n]&&e[n]==r[n])
	{
		printf("0");
		return 0;
	}
	i=1;
	for(j=1;j<=n;j++)
	{
		if(check(i,j))
		{
			for(;i<=j&&check(i,j);i++);
			min=(j-i+2)<min?(j-i+2):min;
		}
	}
	printf("%d",min);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值