bzoj 3238: [Ahoi2013]差异 后缀树

来源:互联网 发布:淘宝店铺永久冻结 编辑:程序博客网 时间:2024/04/29 18:41

       学习了一下后缀自动机转后缀树的方法。虽然可能这道题目后缀数组也可以做。

       考虑后缀自动机的parent,它对应的是比x的right集合略大一点的那个节点;对应到后缀树上,可以发现在缩边后对应的就是y的父亲。

       但是后缀自动机求得实际上是所有前缀倒过来后的后缀树;因此把原串反过来跑sam,然后x连向fa[x]就是后缀树了;每个点的len实际上就是这个节点的深度。

       然后考虑这道题目,本质就是求两两的lcp的长度,那就对每个后缀树上的节点求一下就好了。

AC代码如下:

#include<bits/stdc++.h>#define ll long long#define N 1000005using namespace std;int n; char s[N];struct sam{int cnt,last,ch[N][26],len[N],fa[N],fst[N],nxt[N],sz[N];ll ans;sam(){ cnt=last=1; }void ins(int c){int p=last,np=last=++cnt; len[np]=len[p]+1; sz[cnt]=1;for (; p && !ch[p][c]; p=fa[p]) ch[p][c]=np;if (!p) fa[np]=1; else{int q=ch[p][c];if (len[p]+1==len[q]) fa[np]=q; else{int nq=++cnt; len[nq]=len[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q]));fa[nq]=fa[q]; fa[np]=fa[q]=nq;for (; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;}}}void dfs(int x){int y;for (y=fst[x]; y; y=nxt[y]){dfs(y);ans+=(ll)len[x]*sz[x]*sz[y]; sz[x]+=sz[y];}}void solve(){int i;for (i=2; i<=cnt; i++){nxt[i]=fst[fa[i]]; fst[fa[i]]=i;}dfs(1);printf("%lld\n",((ll)n*(n-1)*(n+1)>>1)-(ans<<1));}}sam;int main(){scanf("%s",s+1); n=strlen(s+1);int i;for (i=n; i; i--) sam.ins(s[i]-'a');sam.solve();return 0;}


by lych

2017.3.16

0 0
原创粉丝点击