P3649 [APIO2014]迴文串
題目描述
給你一個由小寫拉丁字母組成的字串ss。我們定義ss的一個子串的存在值為這個子串在ss中出現的次數乘以這個子串的長度。
對於給你的這個字串ss,求所有迴文子串中的最大存在值。
輸入格式
一行,一個由小寫拉丁字母(a~z)組成的非空字串ss。
輸出格式
輸出一個整數,表示所有迴文子串中的最大存在值。
輸入輸出樣例
輸入 #1abacaba輸出 #1
7輸入 #2
www輸出 #2
4
說明/提示
【樣例解釋1】
用\lvert s \rvert∣s∣表示字串ss的長度。
一個字串s_1 s_2 \dots s_{\lvert s \rvert}s1s2…s∣s∣的子串是一個非空字串s_i s_{i+1} \dots s_jsisi+1…sj,其中1 \leq i \leq j \leq \lvert s \rvert1≤i≤j≤∣s∣。每個字串都是自己的子串。
一個字串被稱作迴文串當且僅當這個字串從左往右讀和從右往左讀都是相同的。
這個樣例中,有77個迴文子串 a,b,c,aba,aca,bacab,abacaba。他們的存在值分別為4, 2, 1, 6, 3, 5, 74,2,1,6,3,5,7。
所以迴文子串中最大的存在值為77。
第一個子任務共 8 分,滿足1 \leq \lvert s \rvert \leq 1001≤∣s∣≤100。
第二個子任務共 15 分,滿足1 \leq \lvert s \rvert \leq 10001≤∣s∣≤1000。
第三個子任務共 24 分,滿足1 \leq \lvert s \rvert \leq 100001≤∣s∣≤10000。
第四個子任務共 26 分,滿足1 \leq \lvert s \rvert \leq 1000001≤∣s∣≤100000。
第五個子任務共 27 分,滿足1 \leq \lvert s \rvert \leq 3000001≤∣s∣≤300000。
題解
首先建立$SAM$的時候儲存一下字串上的每一位對應$SAM$上的哪個節點(也就是將串首到這一位放到$SAM$上跑會跑到哪個節點)
再用倍增處理一下$parent$樹(每個節點的$father$都是與當前子串$right$集合不等價,且是當前子串的最長字尾)
然後跑$manacher$,對於$manacher$過程中的每個迴文中心$i$,在 擴充套件這個迴文中心的半徑$r$的時候,每擴充套件一次,就可以得到一個合法的迴文串$S=(i-r+1,i+r-1)$
如何快速找出迴文串的出現次數呢?我們前面存了字串的每一位對應$SAM$上的哪個點,設這個點為$pos$,那我們就可以得到$pos$代表的子串$(?,i+r-1)$,且這個子串包含迴文串$S$。
然後從$pos$點開始重複往$father$節點跳,每跳一次可以得到當前子串的一個字尾,直到跳到這個字尾是$S=(i-r+1,i+r-1)$為止。這個跳父親節點的操作可以用倍增優化。
程式碼
1 #include<bits/stdc++.h> 2 #define N (600009) 3 #define LL long long 4 using namespace std; 5 6 int f[N][15],len[N],fir[N],n; 7 LL ans; 8 char a[N],s[N]; 9 10 struct SAM { 11 int p,q,np,nq,last,cnt; 12 int son[N][26],fa[N],step[N],right[N],pos[N]; 13 int wt[N],od[N]; 14 SAM() {last=++cnt;} 15 16 void Insert(int x,int id) { 17 p=last; np=last=++cnt; step[np]=step[p]+1; right[np]=1; pos[id]=np; 18 while(!son[p][x] && p) son[p][x]=np, p=fa[p]; 19 if (!p) fa[np]=1; 20 else { 21 q=son[p][x]; 22 if (step[q]==step[p]+1) fa[np]=q; 23 else { 24 nq=++cnt; step[nq]=step[p]+1; 25 memcpy(son[nq],son[q],sizeof(son[q])); 26 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 27 while (son[p][x]==q) son[p][x]=nq, p=fa[p]; 28 } 29 } 30 } 31 void Init() { 32 int len=strlen(a); 33 for (int i=1; i<=cnt; ++i) wt[step[i]]++; 34 for (int i=1; i<=len; ++i) wt[i]+=wt[i-1]; 35 for (int i=cnt; i>=1; --i) od[wt[step[i]]--]=i; 36 for (int i=cnt; i>=1; --i) right[fa[od[i]]]+=right[od[i]]; 37 38 for (int i=1; i<=cnt; ++i) { 39 f[i][0]=fa[i]; 40 for (int j=1; j<=14; ++j) f[i][j]=f[f[i][j-1]][j-1]; 41 } 42 } 43 44 void Check(int l,int r) { 45 if (l<1 || r>n) return; 46 int now=pos[r]; 47 for (int i=14; i>=0; --i) { 48 if (step[f[now][i]]>=r-l+1) now=f[now][i]; 49 ans=max(ans,1ll*right[now]*(r-l+1)); 50 } 51 } 52 }SAM; 53 54 void Manacher() { 55 int tot=0; 56 s[++tot]='@'; s[++tot]='#'; 57 for (int i=0; i<n; ++i) s[++tot]=a[i], fir[tot]=i+1, s[++tot]='#'; 58 s[++tot]='$'; 59 60 int x,ans=0,mid=0,maxn=0; 61 for (int i=1; i<=tot; ++i) { 62 if (i>maxn) x=1; 63 else x=min(maxn-i+1,len[mid*2-i]); 64 SAM.Check(fir[i-x+1],fir[i+x-1]); 65 while (s[i+x]==s[i-x]) ++x, SAM.Check(fir[i-x+1],fir[i+x-1]); 66 len[i]=x; 67 if (i+x-1>maxn) maxn=i+x-1, mid=i; 68 } 69 } 70 71 int main() { 72 cin>>a; 73 n=strlen(a); 74 for (int i=1; i<=n; ++i) SAM.Insert(a[i-1]-'a',i); 75 SAM.Init(); 76 Manacher(); 77 cout<<ans<<endl; 78 }