BZOJ 4236 JOIOJI 排序+线扫

Description

JOIOJI桑是JOI君的叔叔。“JOIOJI”这个名字是由“J、O、I”三个字母各两个构成的。
最近,JOIOJI桑有了一个孩子。JOIOJI桑想让自己孩子的名字和自己一样由“J、O、I”三个字母构成,并且想让“J、O、I”三个字母的出现次数恰好相同。
JOIOJI桑家有一份祖传的卷轴,上面写着一首长诗,长度为N,由“J、O、I”三个字母组成。JOIOJIさん想用诗中最长的满足要求的连续子串作为孩子的名字。
现在JOIOJI桑将这首长诗交给了你,请你求出诗中最长的、包含同样数目的“J、O、I”三个字母的连续子串。

Input

第一行一个正整数N,代表这首长诗的长度
接下来一行一个长度为N的字符串S,表示这首长诗,保证每个字符都是“J、O、I”三个字母中的一个

Output

输出一行一个正整数,代表最长的包含等数量“J、O、I”三个字母的最长连续子串的长度。如果不存在这样的子串,输出0

Sample Input

10
JOIIJOJOOI

Sample Output

6

HINT

选择“IIJOJO”这个子串,长度为6,包含“J、O、I”三个字母各2个,这是最长的满足要求的子串。

1<=N<=2*10^5


Source



乱搜着就点到了这奇怪的题目……
其实比较水的。

思路还是比较简单的,我们用Cj[],Co[],Ci[]来存储J,O,I从1~i的出现次数。
然后题目要求的是最大的R-L,其中R>L且满足:
Cj[R]-Cj[L]=Co[R]-Co[L];
Co[R]-Co[L]=Ci[R]-Ci[L].
微微变形,我们可以得到
Cj[R]-Co[R]=Cj[L]-Co[L];
Co[R]-Ci[R]=Co[L]-Ci[L].
所以说我们用两个数组aJO[u]和aOI[u]来分别存储一下Cj[u]-Co[u]和Co[u]-Ci[u].
我们要找到的就是满足:aJO[L]=aJO[R],且aOI[L]=aOI[R]的L和R。求R-L的最大值。

很显然我们经过特殊的排序技巧可以在线性时间内完成这个工作。
首先记录一下原来的id,然后按照(优先级依次递减):
1.aJO[]从小到大
2.如果1中相等,那么aOI[]从小到大
3.如果2也都相等,那么id[]从小到大。
然后就是一个简单线扫判断了……

写的有点神奇了……1000ms+
而且代码长度看上去还略长= =


#include<bits/stdc++.h>
using namespace std;
const int 
	N=200005;
int n;
int J[N],O[N],I[N];
char t[N];
struct Diff{
	int JO,OI,id;
}a[N];
bool cmp(Diff A,Diff B){
	if (A.JO==B.JO && A.OI==B.OI)
		return A.id<B.id;
	if (A.JO==B.JO)
		return A.OI<B.OI;
	return A.JO<B.JO;
}
int main(){
	scanf("%d",&n);
	scanf("%s",t);
	J[0]=O[0]=I[0]=0;
	for (int i=0;i<n;i++)
		J[i+1]=J[i]+(t[i]=='J'),
		O[i+1]=O[i]+(t[i]=='O'),
		I[i+1]=I[i]+(t[i]=='I');
	for (int i=1;i<=n;i++)
		a[i].JO=J[i]-O[i],a[i].OI=O[i]-I[i],
		a[i].id=i;
	n++; a[n].JO=a[n].OI=a[n].id=0;
	sort(a+1,a+1+n,cmp);
	int past=1,ans=0;
	for (int i=2;i<=n+1;i++){
		if (a[i].JO==a[past].JO && a[i].OI==a[past].OI)
			continue;
		if (a[i].JO!=a[past].JO) ans=max(ans,a[i-1].id-a[past].id),past=i;
			else
		if (a[i].JO==a[past].JO && a[i].OI!=a[past].OI)
			ans=max(ans,a[i-1].id-a[past].id),past=i;
	}
	printf("%d\n",ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值