[bzoj 3676][Apio2014]回文串

Posted PaperCloud

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj 3676][Apio2014]回文串相关的知识,希望对你有一定的参考价值。

传送门

Description

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

Solution

回文树裸题,或者是“回文自动机”?

每个节点都是一个回文串,然后fail指针维护的是它的最大的后缀回文串(同时也是前缀的)。

回文树的用处?

——by (PinkRabbit)

  • 统计每个本质不同回文串出现次数的。这个 Manacher 很难做到(需要配合后缀自动机),但是回文自动机可以解决。比如说本题。
  • 有趣的是,回文自动机可以支持前端插入呢,只需要再维护一个指向最长回文前缀的指针 (head)就好啦。因为回文是两边对称的呢,所以前端插入也没关系的。注意两个指针要同时更新哦。


Code?

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 300005
int fail[MN],len[MN],cnt[MN];
int c[MN][26],en;ll ans;
char s[MN];
int main()
{
    scanf("%s",s+1);
    fail[0]=1;l[++en]=-1;
    for(int i=1,j=1;s[i];++i)
    {
        while(s[i]!=s[i-len[j]-1]) j=fail[j];
        if(!c[j][s[i]-'a'])
        {
            len[++en]=len[j]+2;
            int k=fail[j];
            while(s[i]!=s[i-len[k]-1])
                k=fail[k];    
            fail[en]=c[k][s[i]-'a'];
            c[j][s[i]-'a']=cnt;
        }
        j=c[j][s[i]-'a'];
        ++cnt[j];
    }    
    for(int i=en;i>1;i--)    
    {
        ans=max(ans,1ll*cnt[i]*len[i]);
        cnt[f[i]]+=cnt[i];    
    }
    printf("%lld
",ans);
    return 0;
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

以上是关于[bzoj 3676][Apio2014]回文串的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3676: [Apio2014]回文串

bzoj千题计划304:bzoj3676: [Apio2014]回文串

BZOJ 3676 [Apio2014]回文串(回文树)

BZOJ 3676 APIO2014 回文串

BZOJ3676 [Apio2014]回文串

bzoj 3676 [Apio2014]回文串(Manacher+SAM)