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

 

【題目連接】php

   

    http://www.lydsy.com/JudgeOnline/problem.php?id=3676html

 

【題意】ios

 

    給定一個字符串,定義一個串的權值爲長度*出現次數,求最大權的迴文子串。數組

 

【思路】spa

 

    馬拉車求出本質不一樣的迴文子串。code

    對於一個迴文子串,在SAM中用倍增法在O(logn)的時間獲得它的出現次數,即SAM中每一個節點的right集大小,倍增數組和right均可以經過提早處理獲得。htm

    更新答案便可。blog

 

 先來考慮一個簡單的問題:字符串

給出一個串S(|S|<=1000000)和M個詢問,每次詢問S中[si,ti]這一段串在總串中出現過幾回。
顯然咱們能夠建出後綴數組並用二分+ST表簡單地完成。
但若是咱們必定要用後綴自動機的知識呢?
咱們會發現,假若咱們能快速找到一個節點(狀態)表示當前s~t這一段,只需直接調用它的size便可。
(有關size的預處理:在SAM創建好以後,用全部點的size去累加它的parent的size)
那麼如何快速找到這樣一個狀態呢?
首先SAM有一個性質:把每個節點向它的parent連邊,獲得的樹是原串的逆序串的後綴樹。(只是這棵後綴樹壓縮後的邊權都不知道)
也就是說,若是咱們構建出一棵SAM,它將同時有後綴樹和trie的性質。
舉個例子,好比字符串baabaaa。設詢問爲s=5,t=6
首先畫出對應的後綴樹(空節點再也不畫出):
注意此時後綴樹中「淺」的點表示的是「後綴」。
咱們先跑出從1開始到t的狀態s,此時設咱們在SAM中的節點p。樣例裏p對應在後綴樹的最下面那個點。
可是咱們發現1~t的狀態太長了,咱們只須要s~t的狀態。
這樣咱們能夠在這棵後綴樹上倍增,能往某個祖先跑就往某個祖先跑。能跑的依據就是該祖先的深度>=t-s+1
這樣,咱們就跑到了p節點在後綴樹上的父親的父親,而後直接在SAM裏調用它的信息便可。
這裏還有一個細節問題:若是詢問是s=3,t=6,應該返回哪一個點呢?
這個時候發現不能徹底覆蓋,要不一個點少一些,要不一個點多一些。
顯然要把剩下的部分也選進去,也就是說仍然在p這個點。合法性顯然,且能夠證實這樣最優。
 
                                         Quote from Here

 

【代碼】get

 

 

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 #define rep(a,b,c) for(int a=(b);a>=(c);a--)
 12 using namespace std;
 13 
 14 typedef long long ll;
 15 const int N = 6e5+10;
 16 const int D = 21;
 17 
 18 char s[N];
 19 int n,p[N];
 20 
 21 struct SAM 
 22 {
 23     
 24     ll ans;
 25     int sz,last,ch[N][26],fa[N],R[N],pos[N],l[N],b[N],cnt[N],fat[N][D];
 26     SAM() 
 27     {
 28         sz=ans=0; last=++sz;
 29         memset(cnt,0,sizeof(cnt));
 30         memset(R,0,sizeof(R));
 31         memset(fat,0,sizeof(fat));
 32     }
 33     void add(int c,int id)
 34     {
 35         int np=++sz,p=last; last=np;
 36         l[np]=l[p]+1; R[np]=1; pos[id]=last;
 37         for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
 38         if(!p) fa[np]=1;
 39         else {
 40             int q=ch[p][c];
 41             if(l[q]==l[p]+1) fa[np]=q;
 42             else {
 43                 int nq=++sz; l[nq]=l[p]+1;
 44                 memcpy(ch[nq],ch[q],sizeof(ch[q]));
 45                 fa[nq]=fa[q];
 46                 fa[q]=fa[np]=nq;
 47                 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 
 48             }
 49         }
 50     }
 51     void get_pre()
 52     {
 53         FOR(i,1,sz) cnt[l[i]]++;
 54         FOR(i,1,n) cnt[i]+=cnt[i-1];
 55         rep(i,sz,1) b[cnt[l[i]]--]=i;
 56         rep(i,sz,1) R[fa[b[i]]]+=R[b[i]];
 57         
 58         FOR(i,1,sz) {
 59             fat[i][0]=fa[i];
 60             FOR(j,1,D-1)
 61                 fat[i][j]=fat[fat[i][j-1]][j-1];
 62         }
 63     }
 64     void get_ans(int u,int v)
 65     {
 66         int x=pos[v];
 67         for(int i=D-1;i>=0;i--) {
 68             int t=fat[x][i];
 69             if(l[t]>=v-u+1) x=t;
 70         }
 71         ans=max(ans,(ll)R[x]*(v-u+1));
 72     }
 73     
 74 } sam;
 75 
 76 void Manacher()
 77 {
 78     int mx=0,id;
 79     for(int i=1;i<=n;i++) {
 80         if(mx>i) p[i]=min(mx-i,p[2*id-i-1]);
 81         else p[i]=0;
 82         while(s[i+p[i]+1]==s[i-p[i]]) {
 83             p[i]++;
 84             sam.get_ans(i-p[i]+1,i+p[i]);
 85         }
 86         if(p[i]+i>mx) mx=p[i]+i,id=i;
 87     }
 88     mx=0;
 89     for(int i=1;i<=n;i++) {
 90         if(mx>i) p[i]=min(mx-i-1,p[2*id-i]);
 91         else p[i]=1,sam.get_ans(i,i);
 92         while(s[i+p[i]]==s[i-p[i]]) {
 93             p[i]++;
 94             sam.get_ans(i-p[i]+1,i+p[i]-1);
 95         }
 96         if(p[i]+i>mx) mx=p[i]+i,id=i;
 97     }
 98 }
 99 
100 int main()
101 {
102     scanf("%s",s+1);
103     n=strlen(s+1);
104     FOR(i,1,n) sam.add(s[i]-'a',i);
105     sam.get_pre();
106     s[0]='+',s[n+1]='-';
107     Manacher();
108     printf("%lld",sam.ans);
109     return 0;
110 }
相關文章
相關標籤/搜索