P3649 [APIO2014]回文串
题目描述
给你一个由小写拉丁字母组成的字符串 ss。我们定义 ss 的一个子串的存在值为这个子串在 ss 中出现的次数乘以这个子串的长度。
对于给你的这个字符串 ss,求所有回文子串中的最大存在值。
输入格式
一行,一个由小写拉丁字母(a~z)组成的非空字符串 ss。
输出格式
输出一个整数,表示所有回文子串中的最大存在值。
输入输出样例
abacaba
7
www
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 }