[回文树]JZOJ P3654 回文串

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
这里写图片描述
这里写图片描述

题解

    听题时一脸懵逼,只见dalao什么fail指针连来连去的,目光呆滞的看着黑板(其实心里默默问候了出题人全家)
    首先,这是一颗几乎是裸的回文树
!!但是!!
    回文树是什么?又怎么去实现?
    只好找呀,但基本上没几个看懂的
    最后找到一个Victor Wonder dalao%%%
    才勉强对着标看懂
    具体,我也不怎么会说,自行看dalao写的pdf脑补

链接
链接
(第二个比较详细)

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
int next[300005][30],fail[300005],len[300005],bz[300005],last,n,tot;;
long long cnt[300005]; 
string s;
void newnode(int l)
{
    ++tot;
    for (int i=1;i<=26;i++) next[tot][i]=0;
    len[tot]=l;
    cnt[tot]=0;
}
int get_fail(int x)
{
    while (bz[n-len[x]-1]!=bz[n]) x=fail[x];
    return x;
}
void add(char x)
{
    int c=x-'a'+1;
    bz[++n]=c;
    int cur=get_fail(last);
    if (!next[cur][c])
    {
        newnode(len[cur]+2);
        fail[tot]=next[get_fail(fail[cur])][c];
        next[cur][c]=tot;
    }
    last=next[cur][c];
    cnt[last]++;
}
void count() { for (int i=tot;i>=0;i--) cnt[fail[i]]+=cnt[i]; }
int main()
{
    freopen("palindrome.in","r",stdin);
    freopen("palindrome.out","w",stdout);
    cin>>s;
    tot=-1; bz[n]=-1; fail[0]=1; 
    newnode(0);
    newnode(-1);
    for (int i=0;i<s.length();i++) add(s[i]);
    count();
    long long ans=0;
    for (int i=2;i<=tot;i++)
    {
        long long k=cnt[i]*len[i];
        if (k>ans) ans=k;
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2018-01-31 10:15  BEYang_Z  阅读(166)  评论(0编辑  收藏  举报