1. 程式人生 > >BZOJ 3676 [Apio2014]回文串(回文樹)

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

代碼 nbsp 一個 字符 ast div 題目 cpp sin

【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3676

【題目大意】

  考慮一個只包含小寫拉丁字母的字符串s。
  我們定義s的一個子串t的"出現值"為t在s中的出現次數乘以t的長度。
  求s的所有回文子串中的最大出現值。

【題解】

  我們對給出串建立回文樹,統計每個回文串出現次數和長度,相乘取組大即可

【代碼】

#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
const int N=300010,S=26;
int all,son[N][S],fail[N],cnt[N],len[N],text[N],last,tot;
int newnode(int l){
    for(int i=0;i<S;i++)son[tot][i]=0;
    cnt[tot]=0,len[tot]=l;
    return tot++;
}
void init(){
    last=tot=all=0;
    newnode(0),newnode(-1);
    text[0]=-1,fail[0]=1;
}
int getfail(int x){
    while(text[all-len[x]-1]!=text[all])x=fail[x];
    return x;
}
void add(int w){
    text[++all]=w;
    int x=getfail(last);
    if(!son[x][w]){
        int y=newnode(len[x]+2);
        fail[y]=son[getfail(fail[x])][w];
        son[x][w]=y;
    }cnt[last=son[x][w]]++;
}
void count(){for(int i=tot-1;~i;i--)cnt[fail[i]]+=cnt[i];}
char s[N];
int main(){
    while(~scanf("%s",s)){
        int n=strlen(s); 
        init();
        for(int i=0;i<n;i++)add(s[i]-‘a‘);
        count(); long long ans=0;
        for(int i=0;i<tot;i++)ans=max(ans,1LL*cnt[i]*len[i]);
        printf("%lld\n",ans);
    }return 0;
}

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