P3649 [APIO2014]回文串

思路

回文自动机

回文自动机的fail[i]就是编号为i的这个字符串的最长的回文后缀的编号,然后len[i]表示编号为i的回文串的长度,cnt[i]表示编号为i的回文串的出现次数
然后trans边就是同时在两边加上一个字符,fail边就是跳fail
然后初始有一个0节点,len为0,fail[0]=1,s[0]=-1,表示长度为2的回文串(s[i-1]和s[i]),另一个1节点,len=-1,表示长度为1的回文串(s[i]自身)
然后跳fail就是比较s[i-len[p]-1]==s[i],满足证明找到了,否则继续往上跳
然后子节点的fail就是它父亲的fail的对应转移边(类似AC自动机
然后就没有了

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 300010;
int len[MAXN],cnt[MAXN],fail[MAXN],trans[MAXN][26],s[MAXN],Nodecnt,last,n;
char S[MAXN];
long long ans=0;
int New_state(int _len){
    len[Nodecnt]=_len;
    return Nodecnt++;
}
int get_fail(int p,int n){
    while(s[n-len[p]-1]!=s[n])
        p=fail[p];
    return p;
}
void add_len(int n){
    int cur=get_fail(last,n);
    if(!trans[cur][s[n]]){
        int t=New_state(len[cur]+2);
        fail[t]=trans[get_fail(fail[cur],n)][s[n]];
        trans[cur][s[n]]=t;
    }
    cnt[trans[cur][s[n]]]++;
    last=trans[cur][s[n]];
}
int main(){
    s[0]=-1;
    New_state(0);
    fail[0]=1;
    New_state(-1);
    last=0;
    scanf("%s",S+1);
    n=strlen(S+1);
    for(int i=1;i<=n;i++){
        s[i]=S[i]-'a';
        add_len(i);
    }
    for(int i=Nodecnt-1;i>=0;i--)
        cnt[fail[i]]+=cnt[i];
    for(int i=0;i<Nodecnt;i++)
        ans=max(ans,1LL*cnt[i]*len[i]);
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-04-01 20:23  dreagonm  阅读(177)  评论(0编辑  收藏  举报