bzoj 3676 [Apio2014]回文串(Manacher+SAM)
【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=3676
【题意】
给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串。
【思路】
马拉车求出本质不同的回文子串。
对于一个回文子串,在SAM中用倍增法在O(logn)的时间得到它的出现次数,即SAM中每个节点的right集大小,倍增数组和right都可以通过提前处理得到。
更新答案即可。
先来考虑一个简单的问题:
给出一个串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
【代码】
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 }
posted on 2016-04-03 11:02 hahalidaxin 阅读(695) 评论(0) 编辑 收藏 举报