【[APIO2014]回文串】SAM+manacher+树上倍增 火题
火火火火火(就是一不小心写成了暴力n^2马拉车,一直坑坑坑,还卡我空间,终于卡空间到了127MB,果然我太弱了orz)
BZOJ3676(非权限) Luogu
3676: [Apio2014]回文串
Time Limit: 20 Sec Memory Limit: 128 MB Submit: 3524 Solved: 1632 [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。
Source
貌似听说是回文自动机板题?听说可以跑个SAM然后反串匹配? 算了,搞个暴力的方法,直接上SAM+manacher。 我们先对原串建立自动机,并对parent树建立倍增,再预处理right数组大小。然后跑manacher。当while(a[i+r[i]] == a[i-r[i]]) r[i]++ 的时候我们就知道新出现了一个本质不同的回文串,找到其最后的那个位置在自动机上的位置(提前记录下来),然后在树上跑倍增找到回文长度len,恰好在满足自动机某个结点的min[i]<=len<=max[i],也就是mx[par[i]]<len<=mx[i]的那个结点,ans=max(ans,right[i]*len)。 由于根据manacher的复杂度证明可知其本质不同回文串不会超过n个,所以时间复杂度是有保障的。 卡空间orz orz orz 还有的坑点就是要么你就在manacher的r[i]那里如果i比mx大else r[i]=那里写成r[i]=0,也就是从0本身开始找回文(就是本身len=1开始找回文)要么就在最后扫一下,特判长度为1时的回文。(数据abca,输出2) 就是酱紫了。 code:/* 搞出right数组,倍增数组 先建自动机,记录最后一个出现位置 跑马拉车,找出对应本质不同回文串的地方和出现几次,加入贡献 */ #include<stdio.h> #include<bits/stdc++.h> using namespace std; const int maxn = 600005; int tot=1,las=1,rt=1,mx[maxn],son[maxn][26],par[maxn],last[maxn]; long long riri[maxn]; int push(int val) { mx[++tot] = val; return tot; } int ton[maxn/2],pp[maxn]; void extend(int t,int k) { int np,nq,p,q; np = push(mx[las]+1); for(p=las;p&&(!son[p][t]);p=par[p]) son[p][t]=np; if(!p) par[np] = rt; else { q = son[p][t]; if(mx[p]+1==mx[q]) { par[np] = q; } else { nq = push(mx[p]+1); memcpy(son[nq],son[q],sizeof(son[q])); par[nq] = par[q]; par[q] = par[np] = nq; for(;p&&son[p][t]==q;p=par[p]) son[p][t] = nq; } } las = np; last[k]=las; riri[las] = 1; } int fa[maxn][20],dep[maxn]; int fi(int x,int len) { // if(mx[x]>=len&&mx[par[x]]<len) return x; for(int i=18;i>=0;i--) { if(mx[fa[x][i]]>=len) x = fa[x][i]; } return x; } char ss[maxn]; long long ans; int rr[maxn]; int main() { int len=0; ss[0]='('; ss[1]='*'; while(233) { char ch = getchar(); if(ch>'z'||ch<'a') break; ++len; ss[len*2]=ch; ss[len*2-1]='*'; extend(ss[len*2]-'a',len*2); } for(int i=1;i<=tot;i++) ton[mx[i]]++; for(int i=1;i<=len;i++) ton[i]+=ton[i-1]; for(int i=1;i<=tot;i++) pp[ ton[mx[i]]-- ] = i; for(int i=tot;i>=1;i--) riri[par[pp[i]]]+=riri[pp[i]]; for(int i=1;i<=tot;i++) { fa[pp[i]][0]=par[pp[i]]; for(int j=1;j<=18;j++) fa[pp[i]][j]=fa[fa[pp[i]][j-1]][j-1]; } ss[len*2+1]='*'; ss[len*2+2]=')'; int mmx=0,ppi=0; for(int i=1;i<=len*2+1;i++) { // cout<<i<<' '; if(i<mmx) rr[i] = min(mmx-i,rr[ppi*2-i]); else rr[i]=1; while( ss[ rr[i] + i ] == ss[ -rr[i] + i ] ) { rr[i]++; if(ss[i+rr[i]-1]!='*') { int lala = last[i+rr[i]-1],lll = rr[i]; int kk = fi(lala,lll); ans = max ( (long long)riri[kk]*lll , ans ); } } if(i+rr[i]-1>mmx) { mmx = i+rr[i]-1; ppi = i; } } for(int i=1;i<=tot;i++) { if(par[i]==rt) ans = max(ans,riri[i]); } printf("%lld",ans); }