【[APIO2014]回文串】SAM+manacher+树上倍增 火题

火火火火火(就是一不小心写成了暴力n^2马拉车,一直坑坑坑,还卡我空间,终于卡空间到了127MB,果然我太弱了orz) BZOJ3676(非权限)  Luogu  

3676: [Apio2014]回文串

Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 3524  Solved: 1632 [Submit][Status][Discuss]

Description

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最 大出现值。

Input

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。

Output

输出一个整数,为逝查回文子串的最大出现值。

Sample Input

【样例输入l】 abacaba【样例输入2] www  

Sample Output

【样例输出l】 7【样例输出2] 4

HINT

一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。 在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中: ● a出现4次,其出现值为4:1:1=4 ● b出现2次,其出现值为2:1:1=2 ● c出现1次,其出现值为l:1:l=l ● aba出现2次,其出现值为2:1:3=6 ● aca出现1次,其出现值为1=1:3=3 ●bacab出现1次,其出现值为1:1:5=5 ● abacaba出现1次,其出现值为1:1:7=7 故最大回文子串出现值为7。 【数据规模与评分】 数据满足1≤字符串长度≤300000。

Source

貌似听说是回文自动机板题?听说可以跑个SAM然后反串匹配? 算了,搞个暴力的方法,直接上SAM+manacher。 我们先对原串建立自动机,并对parent树建立倍增,再预处理right数组大小。然后跑manacher。当while(a[i+r[i]] == a[i-r[i]]) r[i]++ 的时候我们就知道新出现了一个本质不同的回文串,找到其最后的那个位置在自动机上的位置(提前记录下来),然后在树上跑倍增找到回文长度len,恰好在满足自动机某个结点的min[i]<=len<=max[i],也就是mx[par[i]]<len<=mx[i]的那个结点,ans=max(ans,right[i]*len)。 由于根据manacher的复杂度证明可知其本质不同回文串不会超过n个,所以时间复杂度是有保障的。 卡空间orz orz orz 还有的坑点就是要么你就在manacher的r[i]那里如果i比mx大else r[i]=那里写成r[i]=0,也就是从0本身开始找回文(就是本身len=1开始找回文)要么就在最后扫一下,特判长度为1时的回文。(数据abca,输出2) 就是酱紫了。 code:
/*
搞出right数组,倍增数组
先建自动机,记录最后一个出现位置
跑马拉车,找出对应本质不同回文串的地方和出现几次,加入贡献 
*/
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 600005;
int tot=1,las=1,rt=1,mx[maxn],son[maxn][26],par[maxn],last[maxn];
long long riri[maxn];
int push(int val)
{
	mx[++tot] = val;
	return tot;
}
int ton[maxn/2],pp[maxn];
void extend(int t,int k)
{
	int np,nq,p,q;
	np = push(mx[las]+1);
	for(p=las;p&&(!son[p][t]);p=par[p]) son[p][t]=np;
	if(!p) par[np] = rt;
	else
	{
		q = son[p][t];
		if(mx[p]+1==mx[q])
		{
			par[np] = q;
		}
		else
		{
			nq = push(mx[p]+1);
			memcpy(son[nq],son[q],sizeof(son[q]));
			par[nq] = par[q]; par[q] = par[np] = nq;
			for(;p&&son[p][t]==q;p=par[p]) son[p][t] = nq;
		}
	}
	las = np;
	last[k]=las;
	riri[las] = 1;
} 
int fa[maxn][20],dep[maxn];
int fi(int x,int len)
{
//	if(mx[x]>=len&&mx[par[x]]<len) return x;
	for(int i=18;i>=0;i--)
	{
		if(mx[fa[x][i]]>=len) x = fa[x][i];
	}
	return x;
}
char ss[maxn];
long long ans;
int rr[maxn];
int main()
{
	int len=0;
	ss[0]='('; ss[1]='*';
	while(233)
	{
		char ch = getchar();
		if(ch>'z'||ch<'a') break;
		++len;
		ss[len*2]=ch;
		ss[len*2-1]='*';
		extend(ss[len*2]-'a',len*2);
	}
	for(int i=1;i<=tot;i++) ton[mx[i]]++;
	for(int i=1;i<=len;i++) ton[i]+=ton[i-1];
	for(int i=1;i<=tot;i++) pp[ ton[mx[i]]-- ] = i;
	for(int i=tot;i>=1;i--) riri[par[pp[i]]]+=riri[pp[i]];
	for(int i=1;i<=tot;i++) 
	{
		fa[pp[i]][0]=par[pp[i]];
		for(int j=1;j<=18;j++) fa[pp[i]][j]=fa[fa[pp[i]][j-1]][j-1];
	}
	ss[len*2+1]='*'; ss[len*2+2]=')';
	int mmx=0,ppi=0;
	for(int i=1;i<=len*2+1;i++)
	{
	//	cout<<i<<' ';
		if(i<mmx) rr[i] = min(mmx-i,rr[ppi*2-i]);
		else rr[i]=1;
		while( ss[ rr[i] + i ] == ss[ -rr[i] + i ] )
		{
			rr[i]++;
			if(ss[i+rr[i]-1]!='*')
			{
				int lala = last[i+rr[i]-1],lll = rr[i];
				int kk = fi(lala,lll);
				ans = max ( (long long)riri[kk]*lll , ans );
			}
		}
		if(i+rr[i]-1>mmx)
		{
			mmx = i+rr[i]-1;
			ppi = i;
		}
	}
	for(int i=1;i<=tot;i++)
	{
		if(par[i]==rt) ans = max(ans,riri[i]);
	}
	printf("%lld",ans);
}
   
posted @ 2018-06-02 09:36  Newuser233  阅读(6)  评论(0)    收藏  举报