天天看点

回文树(回文自动机) - BZOJ 3676 回文串  http://www.lydsy.com/JudgeOnline/problem.php?id=3676

  BZOJ 3676 回文串

Problem's Link:  http://www.lydsy.com/JudgeOnline/problem.php?id=3676

Mean: 

analyse:

由于构造完回文自动机后,len[i]表示第i个回文串的长度,cnt[i]表示第i个回文串出现的次数,只需两者相乘去最大就可。

注意:i是从2开始,因为有两个长度为0和长度为-1的节点。

Time complexity: O(N)

Source code: 

/*

* this code is made by crazyacking

* Verdict: Accepted

* Submission Date: 2015-08-19-13.56

* Time: 0MS

* Memory: 137KB

*/

#include <queue>

#include <cstdio>

#include <set>

#include <string>

#include <stack>

#include <cmath>

#include <climits>

#include <map>

#include <cstdlib>

#include <iostream>

#include <vector>

#include <algorithm>

#include <cstring>

#define  LL long long

#define  ULL unsigned long long

using namespace std;

const int MAXN = 300005 ;

const int N = 26 ;

char s[MAXN];

struct Palindromic_Tree

{

     int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成

     int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点

     int cnt[MAXN] ;

     int num[MAXN] ;

     int len[MAXN] ;//len[i]表示节点i表示的回文串的长度

     int S[MAXN] ;//存放添加的字符

     int last ;//指向上一个字符所在的节点,方便下一次add

     int n ;//字符数组指针

     int p ;//节点指针

     int newnode(int l)     //新建节点

     {

           for(int i = 0 ; i < N ; ++ i) next[p][i] = 0 ;

           cnt[p] = 0 ;

           num[p] = 0 ;

           len[p] = l ;

           return p ++ ;

     }

     void init()   //初始化

           p = 0 ;

           newnode(0) ;

           newnode(-1) ;

           last = 0 ;

           n = 0 ;

           S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判

           fail[0] = 1 ;

     int get_fail(int x)     //和KMP一样,失配后找一个尽量最长的

           while(S[n - len[x] - 1] != S[n]) x = fail[x] ;

           return x ;

     void add(int c)

           c -= 'a' ;

           S[++ n] = c ;

           int cur = get_fail(last) ;   //通过上一个回文串找这个回文串的匹配位置

//            cout<<cur<<endl;

           if(!next[cur][c])     //如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串

           {

                 int now = newnode(len[cur] + 2) ;   //新建节点

//                  cout<<get_fail(fail[cur])<<endl;

                 fail[now] = next[get_fail(fail[cur])][c] ;   //和AC自动机一样建立fail指针,以便失配后跳转

//                  cout<<fail[now]<<endl;

                 next[cur][c] = now ;

                 num[now] = num[fail[now]] + 1 ;

           } last = next[cur][c] ;

//            cout<<last<<endl;

           cnt[last] ++ ;

     void count()

           for(int i = p - 1 ; i >= 0 ; -- i) cnt[fail[i]] += cnt[i] ;

           //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!

} run;

int main()

     scanf("%s",&s);

     int n=strlen(s);

     run.init();

     for(int i=0; i<n; i++) run.add(s[i]);

     run.count();

     LL ans=LLONG_MIN;

     for(int i=2; i<run.p; ++i)

           ans=max(ans,(LL)run.cnt[i]*run.len[i]);

     cout<<ans<<endl;

     return 0;

}

      |  Github |  Facebook  |  Twitter |