【BZOJ4236】JOIOJI STL

【BZOJ4236】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

题解:本题要求三个字符的数量都相同,我们仍然采用前缀相减的方式,设sj,so,si表示J,O,I的前缀出现次数,那么条件就是:

sj[i]-sj[j]=so[i]-so[j]=si[i]-si[j]

移项,得

sj[i]-so[i]=sj[j]-so[j],sj[i]-si[i]=si[i]-si[j]

这样就转变成让i和j的两个权值都相等的问题,用map存一下就好了

然而我一开始忘了map怎么用,手写treap水过~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
using namespace std;
const int maxn=200010;
int rt[maxn<<1],ch[maxn][2],v[maxn],k[maxn],w[maxn];
int n,tot,s1,s2,s3,ans;
char str[maxn];
void rotate(int &x,int d)
{
	int y=ch[x][d];
	ch[x][d]=ch[y][d^1];
	ch[y][d^1]=x;
	x=y;
}
void insert(int &x,int y,int z)
{
	if(!x)
	{
		x=++tot;
		v[x]=y,w[x]=z,k[x]=rand();
		return ;
	}
	int d=y<v[x]?0:1;
	insert(ch[x][d],y,z);
	if(k[ch[x][d]]>k[x])	rotate(x,d);
}
int find(int x,int y)
{
	if(!x)	return 0;
	if(y==v[x])	return x;
	if(y<v[x])	return find(ch[x][0],y);
	return find(ch[x][1],y);
}
int main()
{
	srand(2333333);
	scanf("%d",&n);
	scanf("%s",str);
	int i;
	insert(rt[n],0,0);
	for(i=1;i<=n;i++)
	{
		if(str[i-1]=='J')	s1++;
		if(str[i-1]=='O')	s2++;
		if(str[i-1]=='I')	s3++;
		int t=find(rt[s1-s2+n],s2-s3);
		if(!t)	insert(rt[s1-s2+n],s2-s3,i);
		else	ans=max(ans,i-w[t]);
	}
	printf("%d",ans);
	return 0;
}
posted @ 2017-03-25 15:28  CQzhangyu  阅读(455)  评论(0编辑  收藏  举报