[bzoj3676] [Apio2014] 回文串

Description

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

Input

输入只有一行,为一个只包含小写字母 \(a-z\) 的非空字符串 \(s\)

Output

输出一个整数,为逝查回文子串的最大出现值。

Sample Input

【样例输入1】

abacaba

【样例输入2】

www

Sample Output

【样例输出1】

7

【样例输出2】

4

HINT

一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。

在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:

● a出现4次,其出现值为4:1:1=4

● b出现2次,其出现值为2:1:1=2

● c出现1次,其出现值为l:1:l=l

● aba出现2次,其出现值为2:1:3=6

● aca出现1次,其出现值为1=1:3=3

●bacab出现1次,其出现值为1:1:5=5

● abacaba出现1次,其出现值为1:1:7=7

故最大回文子串出现值为7。

【数据规模与评分】

数据满足1≤字符串长度≤300000。


想法

做法一:回文树

算是模板题吧。
由于本质不同的回文串个数不超过 \(|S|\) ,我们考虑统计每个回文串出现次数。
考虑在增量建树时,每增一个点都找到了以它为结尾最长回文后缀 \(x\) 及 其 \(fail\) 值,那么加入这个点,增加的回文串就是 \(x\)\(x\) 一路 \(fail\) 上去经过的所有节点。
但每增一个点都统计一遍有点慢,于是记下来最后在 \(fail\) 树上统一 \(dp\) 出答案即可。

做法二:后缀自动机+ \(manacher\)

先把字符串加到后缀自动机中,预处理 \(endpos\) 集合大小,即子串出现次数。
进行 \(manacher\) 算法时,如果 \(mx\) 往后扩展了,说明可能形成了一个本质不同的字符串,于是用后缀自动机统计它的出现次数。
\(parent\) 树上倍增找到该结点即可。


代码

回文树

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 300005;
typedef long long ll;

char s[N];
int n;

int cnt,fail[N],len[N],t[N],ch[N][26],last;
void init(){ fail[0]=fail[1]=1; len[1]=-1; last=cnt=1; }
void insert(int i,int c){
    int p=last;
    for(;s[i-len[p]-1]!=s[i];) p=fail[p];
    if(ch[p][c]) { t[ch[p][c]]++;/**/ last=ch[p][c]; return; }
    int x=++cnt,k=fail[p];
    for(;s[i-len[k]-1]!=s[i];) k=fail[k];
    fail[x]=ch[k][c]; 
    len[x]=len[p]+2; ch[p][c]=x;
    t[x]++; last=x;
}

int in[N],que[N],hd,tl;
ll ans;/**/
void cal(){
    for(int i=2;i<=cnt;i++) in[fail[i]]++;
    hd=tl=0;
    for(int i=2;i<=cnt;i++) if(in[i]==0) que[tl++]=i;
    while(hd<tl){
        int x=que[hd++];
        if(x==1) break;
        ans=max(ans,1ll*t[x]*len[x]);
        t[fail[x]]+=t[x]; in[fail[x]]--;
        if(in[fail[x]]==0) que[tl++]=fail[x];
    }
}

int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    
    init();
    for(int i=1;i<=n;i++) insert(i,s[i]-'a');
    cal();
    
    printf("%lld\n",ans);
    
    return 0;
} 

[bzoj3676] [Apio2014] 回文串

上一篇:在LabWindows/CVI中能同时读写一个文件吗?


下一篇:WPF 画一个3D矩形并旋转