BZOJ3676 APIO2014回文串(manacher+后缀自动机)

  由于本质不同的回文子串数量是O(n)的,考虑在对于每个回文子串在第一次找到它时对其暴力统计。可以发现manacher时若右端点移动则找到了一个新回文串。注意这样会漏掉串长为1的情况,特判一下。

  现在问题变为统计一个子串的出现次数。可以用SA,二分乱搞一下即可。这里使用SAM。以parent树上表示该子串的节点为起点,用倍增往上跳,找到深度最小的满足len限制的点就好了,出现次数就是其right集合的大小。

  uojAC,luoguRE一个点,bzojMLE……

 

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 600010
int n,last,cnt,len[N],fail[N],son[N][26],size[N],pos[N>>1],p[N];
long long ans=0;
char s[N];
namespace tree
{
    int p[N],t=0,fa[N][20];
    struct data{int to,nxt;
    }edge[N];
    void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
    void dfs(int k)
    {
        for (int i=p[k];i;i=edge[i].nxt)
        {
            fa[edge[i].to][0]=k;
            dfs(edge[i].to);
            size[k]+=size[edge[i].to];
        }
    }
    void build()
    {
        for (int i=2;i<=cnt;i++) addedge(fail[i],i);
        fa[1][0]=1;dfs(1);
        for (int j=1;j<20;j++)
            for (int i=1;i<=cnt;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    }
    int calc(int l,int r)
    {
        int x=pos[r];
        for (int j=19;~j;j--) if (len[fa[x][j]]>=r-l+1) x=fa[x][j];
        return size[x];
    }
}
using tree::calc;
void ins(int c,int n)
{
    int x=++cnt,p=last;last=x;len[x]=n;size[x]=1;pos[n]=x;
    while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
    if (!p) fail[x]=1;
    else
    {
        int q=son[p][c];
        if (len[p]+1==len[q]) fail[x]=q;
        else
        {
            int y=++cnt;len[y]=len[p]+1;
            memcpy(son[y],son[q],sizeof(son[q]));
            fail[y]=fail[q];fail[q]=fail[x]=y;
            while (son[p][c]==q) son[p][c]=y,p=fail[p];
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj3676.in","r",stdin);
    freopen("bzoj3676.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    last=cnt=1;
    scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;i++) ins(s[i]-97,i);
    tree::build();
    for (int i=n;i>=1;i--) s[i*2-1]=s[i];
    for (int i=1;i<n;i++) s[i<<1]='$';
    int x=1;
    for (int i=1;i<=n;i++) ans=max(ans,1ll*calc(i,i));
    for (int i=2;i<n*2;i++) 
    {
        if (x+p[x]>i) p[i]=min(x+p[x]-i,p[x-(i-x)]);
        while (i-p[i]-1>=1&&i+p[i]+1<n*2&&s[i+p[i]+1]==s[i-p[i]-1]) 
        {
            p[i]++;
            if (s[i+p[i]]!='$') ans=max(ans,1ll*((i+p[i]>>1)-(i-p[i]>>1)+1)*calc((i-p[i]>>1)+1,(i+p[i]>>1)+1));
        }
        if (i+p[i]>x+p[x]) x=i;
    }
    cout<<ans;
    return 0;
}

 

posted @ 2018-08-24 20:06  Gloid  阅读(176)  评论(0编辑  收藏  举报