您的位置:首页 > 其它

bzoj 3676: [Apio2014]回文串 manachar+后缀自动机+倍增(回文树)

2017-04-09 20:47 519 查看

题意

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

n<=300000

分析

因为自己对马拉车的性质不太熟悉,所以一开始没做出来。

考虑在跑马拉车算法的时候,所有本质不同的回文串必然包含在所有能使mx增加的回文串内,也就是最多只有O(n)个。那么我们只要求出后缀自动机,然后每找到一个回文串就扔进后缀自动机里跑一下就好了。

但是由于找祖先太慢,我们可以先预处理,然后倍增找即可。

后来才发现这是回文树的裸题。然后就去学习了一发。回文树介绍

代码

后缀自动机

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long LL;

const int N=300005;
const int M=N*2;

int n,pos
,mx[M],c[M],b
,f[M],fa[M][19],ch[M][26],last,cnt,l[N*2];
char a[N*2],s
;
LL ans;

void ins(int x,int id)
{
int p,q,np,nq;
p=last;last=np=++cnt;mx[np]=mx[p]+1;f[np]=1;pos[id]=np;
for (;!ch[p][x]&&p;p=fa[p][0]) ch[p][x]=np;
if (!p) fa[np][0]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np][0]=q;
else
{
nq=++cnt;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq][0]=fa[q][0];
fa[q][0]=fa[np][0]=nq;
for (;ch[p][x]==q;p=fa[p][0]) ch[p][x]=nq;
}
}
}

void prework()
{
for (int i=1;i<=cnt;i++) b[mx[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=1;i<=cnt;i++) c[b[mx[i]]--]=i;
for (int i=cnt;i>=1;i--) f[fa[c[i]][0]]+=f[c[i]];
for (int i=1;i<=cnt;i++)
for (int j=1;j<=18;j++) fa[c[i]][j]=fa[fa[c[i]][j-1]][j-1];
}

void query(int l,int r)
{
if (a[l]=='#') return;
l/=2;r/=2;
int now=pos[r];
for (int i=18;i>=0;i--) if (mx[fa[now][i]]>=r-l+1) now=fa[now][i];
ans=max(ans,(LL)(r-l+1)*f[now]);
}

void manachar()
{
for (int i=1;i<=n*2+1;i++)
if (i%2==1) a[i]='#';
else a[i]=s[i/2];
int mx=0,id;
for (int i=1;i<=n*2+1;i++)
{
if (i<=mx) l[i]=min(mx-i+1,l[id*2-i]);
while (i+l[i]<=n*2+1&&i+l[i]>=1&&a[i+l[i]]==a[i-l[i]])
{
l[i]++;
if (i+l[i]-1>mx) mx=i+l[i]-1,id=i,query(i-l[i]+1,i+l[i]-1);
}
}
}

int main()
{
scanf("%s",s+1);
n=strlen(s+1);
cnt=last=1;
for (int i=1;i<=n;i++) ins(s[i]-'a',i);
prework();
manachar();
printf("%lld",ans);
return 0;
}


回文树

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N=300005;

int n;
char s
;

struct pam
{
int ch
[26],size
,fail
,len
,last,sz;
pam()
{
len[1]=-1;fail[0]=fail[1]=1;sz=1;
}
void ins(int c,int n)
{
int p=last;
while (s[n-1-len[p]]!=s
) p=fail[p];
if (!ch[p][c])
{
int now=++sz,k=fail[p];len[now]=len[p]+2;
while (s[n-1-len[k]]!=s
) k=fail[k];
fail[now]=ch[k][c];ch[p][c]=now;
}
last=ch[p][c];size[last]++;
}
void solve()
{
LL ans=0;
for (int i=sz;i>=1;i--) size[fail[i]]+=size[i],ans=max(ans,(LL)size[i]*len[i]);
printf("%lld",ans);
}
}pam;

int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for (int i=1;i<=n;i++) pam.ins(s[i]-'a',i);
pam.solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: