BZOJ3676: [Apio2014]回文串(后缀自动机+manacher/回文自动机)

来源:互联网 发布:ipad广告拦截软件 编辑:程序博客网 时间:2024/06/04 18:15

传送门

题意:
给一个字符串s。定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。求出s的所有回文子串中的最
大出现值。

题解:
manacher求出所有本质不同的回文子串后在后缀自动机上二分。

(网上说的什么回文自动机感觉不清真啊。。)

#include<bits/stdc++.h>using namespace std;const int Maxn=6e5+50;char ch[Maxn],S[Maxn];int pos[Maxn],n,tot;long long ans;struct SAM{    int last,len[Maxn],cnt,link[Maxn][21],f[Maxn],son[Maxn][26],c[Maxn],que[Maxn];    SAM()    {        last=++cnt;    };    inline void extend(int c,int o)    {        int p=last,np=last=++cnt;len[np]=len[p]+1;        f[np]=1;pos[o]=np;        while(!son[p][c]&&p)son[p][c]=np,p=link[p][0];        if(!p)link[np][0]=1;        else        {            int q=son[p][c];            if(len[q]==len[p]+1)link[np][0]=q;            else            {                int nq=++cnt;len[nq]=len[p]+1;                memcpy(son[nq],son[q],sizeof(son[q]));                link[nq][0]=link[q][0];link[q][0]=link[np][0]=nq;                while(p&&son[p][c]==q)son[p][c]=nq,p=link[p][0];            }        }    }    inline void init()    {        for(int i=1;i<=cnt;i++)c[len[i]]++;        for(int i=1;i<=n;i++)c[i]+=c[i-1];        for(int i=cnt;i>=1;i--)que[c[len[i]]--]=i;        for(int i=cnt;i>=1;i--)        {            int u=que[i];            if(link[u][0])f[link[u][0]]+=f[u];        }        for(int i=1;i<=cnt;i++)        {            int u=que[i];            for(int j=1;j<=20;j++)link[u][j]=link[link[u][j-1]][j-1];        }    }    inline void query(int l,int r)    {        if(l&1)l++;if(r&1)r--;        if(l>r)return;        l/=2;r/=2;        int p=pos[r],L=r-l+1;        for(int i=20;i>=0;i--)        {            int y=link[p][i];            if(len[y]>=L)p=link[p][i];        }        ans=max(ans,1ll*L*f[p]);    }}sam;inline void manacher(){    static int R[Maxn];    int pos=1,mx=1;    for(int i=2;i<tot;i++)    {        if(i>=mx)R[i]=1,sam.query(i,i);        else R[i]=min(mx-i,R[pos*2-i]);        while(S[i+R[i]]==S[i-R[i]])        {            R[i]++;            int l=i-R[i]+1,r=i+R[i]-1;            sam.query(l,r);        }        if(i+R[i]>mx)mx=i+R[i],pos=i;    }}int main(){    scanf("%s",ch+1);    n=strlen(ch+1);    for(int i=1;i<=n;i++)sam.extend(ch[i]-'a',i);    sam.init();    S[0]='!';S[++tot]='#';    for(int i=1;i<=n;i++)S[++tot]=ch[i],S[++tot]='#';    S[++tot]='?';    manacher();    printf("%lld",ans);}

回文自动机:

#include<bits/stdc++.h>using namespace std;const int Maxn=3e5+50;char ch[Maxn],S[Maxn];int n,m,cnt,fail[Maxn],last,len[Maxn],son[Maxn][26],f[Maxn];inline int findfail(int x){    while(S[m-len[x]-1]!=S[m])x=fail[x];    return x;}int main(){    scanf("%s",ch+1);    n=strlen(ch+1);    S[0]='!';    fail[0]=1;last=0;len[1]=-1;    cnt=1;    for(int i=1;i<=n;i++)    {        S[++m]=ch[i];        int t=findfail(last);        int c=ch[i]-'a';        if(!son[t][c])        {            ++cnt;            len[cnt]=len[t]+2;            fail[cnt]=son[findfail(fail[t])][c];            son[t][c]=cnt;        }        last=son[t][c];        f[last]++;    }    long long ans=0;    for(int i=cnt;i>=0;i--)    {        ans=max(ans,1ll*len[i]*f[i]);        f[fail[i]]+=f[i];    }    cout<<ans<<endl;}
阅读全文
0 0