[BZOJ3676][APIO2014]回文串(Manacher+SAM)
3676: [Apio2014]回文串
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 3097 Solved: 1408
[Submit][Status][Discuss]
Description
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。
Input
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
Output
输出一个整数,为逝查回文子串的最大出现值。
Sample Input
【样例输入l】
abacaba
【样例输入2]
www
Sample Output
【样例输出l】
7
【样例输出2]
4
HINT
一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。
在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中:
● a出现4次,其出现值为4:1:1=4
● b出现2次,其出现值为2:1:1=2
● c出现1次,其出现值为l:1:l=l
● aba出现2次,其出现值为2:1:3=6
● aca出现1次,其出现值为1=1:3=3
●bacab出现1次,其出现值为1:1:5=5
● abacaba出现1次,其出现值为1:1:7=7
故最大回文子串出现值为7。
【数据规模与评分】
数据满足1≤字符串长度≤300000。
代码总用时:3h
很简单的一道题,只要意识到Manacher算法的本质(本质不同的回文串的个数是O(n)的),配合后缀自动机或者后缀数组就可以轻松解决。
但这道题调了好久,浪费了很多时间,一是因为后缀自动机模板不熟练,而是Manacher算法流程没有一个清楚的认识。
写代码的时候精力要高度集中,不能因为低级错误耽误时间。
下面是SAM版本的代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=l; i<=r; i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=600100; 9 int cnt=1,lst=1,n,tot[N],mx[N],p[N],pos[N],son[N][27],fa[N],f[N][20],q[N],R[N]; 10 ll ans; char s[N],S[N]; 11 12 void ext(int c,int x){ 13 int p=lst,np=lst=++cnt; mx[np]=mx[p]+1; R[np]=1; pos[x]=np; 14 while (!son[p][c] && p) son[p][c]=np,p=fa[p]; 15 if (!p) fa[np]=1; 16 else{ 17 int q=son[p][c]; 18 if (mx[q]==mx[p]+1) fa[np]=q; 19 else{ 20 int nq=++cnt; mx[nq]=mx[p]+1; 21 memcpy(son[nq],son[q],sizeof(son[q])); 22 fa[nq]=fa[q]; fa[q]=fa[np]=nq; 23 while (son[p][c]==q && p) son[p][c]=nq,p=fa[p]; 24 } 25 } 26 } 27 28 void pre(){ 29 rep(i,1,cnt) tot[mx[i]]++; 30 rep(i,1,n) tot[i]+=tot[i-1]; 31 for (int i=cnt; i; i--) q[tot[mx[i]]--]=i; 32 for (int i=cnt; i; i--) R[fa[q[i]]]+=R[q[i]]; 33 rep(i,1,cnt){ 34 f[i][0]=fa[i]; 35 rep(j,1,19) f[i][j]=f[f[i][j-1]][j-1]; 36 } 37 } 38 39 void get(int l,int r){ 40 l=(l>>1)+(l&1); r>>=1; int x=pos[r]; 41 for (int i=19; ~i; i--) 42 if (mx[f[x][i]]>=r-l+1) x=f[x][i]; 43 ans=max(ans,1ll*R[x]*(r-l+1)); 44 } 45 46 void manacher(){ 47 int mxlen=0,id; 48 rep(i,1,n){ 49 if (mxlen>i) p[i]=min(mxlen-i,p[2*id-i]); 50 else{ p[i]=1; if (S[i]!='#') get(i,i); } 51 while (S[i+p[i]]==S[i-p[i]]) get(i-p[i],i+p[i]),p[i]++; 52 if (p[i]+i>mxlen) mxlen=p[i]+i,id=i; 53 } 54 } 55 56 int main(){ 57 freopen("palindromes.in","r",stdin); 58 freopen("palindromes.out","w",stdout); 59 scanf("%s",s+1); n=strlen(s+1); 60 rep(i,1,n) ext(s[i]-'a',i); 61 pre(); S[0]='$'; S[1]='#'; 62 rep(i,1,n) S[(i<<1)+1]='#',S[i<<1]=s[i]; 63 n=(n<<1)+1; manacher(); printf("%lld\n",ans); 64 return 0; 65 }