【BZOJ3238】差异【后缀自动机+dp】
题意
分析
这个题目还是很优秀的。sigma(len(Ti)+len(Tj))的值是一定的=n*(n+1)*(n-1)/2。那么关键就是求任意两个后缀的lcp的和了。
我们怎么求两个后缀的lcp?如果用后缀自动机的话,我们可以先把字符串反过来,然后建后缀自动机,那么两个后缀的lcp就是他们两个在parent树上的最近公共祖先(lca)的len。我们要求的是任意两个后缀的lcp的和,我们可以考虑在parent上跑树形dp。令dp[u]为以u为lca的lcp的和。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 typedef long long LL; 8 const int maxn=500000+100; 9 int n; 10 char s[maxn]; 11 struct state{ 12 int len,link; 13 int next[26]; 14 }st[2*maxn]; 15 int last,cur,sz; 16 int c[2*maxn],cnt[2*maxn]; 17 LL dp[2*maxn]; 18 void init(){ 19 sz=1; 20 last=cur=0; 21 st[0].link=-1; 22 st[0].len=0; 23 } 24 void build_sam(int c){ 25 cur=sz++; 26 cnt[cur]=1; 27 st[cur].len=st[last].len+1; 28 int p; 29 for(p=last;p!=-1&&st[p].next[c]==0;p=st[p].link) 30 st[p].next[c]=cur; 31 if(p==-1) 32 st[p].link=0; 33 else{ 34 int q=st[p].next[c]; 35 if(st[q].len==st[p].len+1) 36 st[cur].link=q; 37 else{ 38 int clone=sz++; 39 st[clone].len=st[p].len+1; 40 st[clone].link=st[q].link; 41 for(int i=0;i<26;i++) 42 st[clone].next[i]=st[q].next[i]; 43 for(;p!=-1&&st[p].next[c]==q;p=st[p].link) 44 st[p].next[c]=clone; 45 st[cur].link=st[q].link=clone; 46 } 47 } 48 last=cur; 49 } 50 int cmp(int a,int b){ 51 return st[a].len>st[b].len; 52 } 53 int main(){ 54 scanf("%s",s); 55 n=strlen(s); 56 init(); 57 for(int i=0;i<n;i++) 58 build_sam(s[i]-'a'); 59 for(int i=0;i<sz;i++) 60 c[i]=i; 61 sort(c,c+sz,cmp ); 62 for(int i=0;i<sz;i++){ 63 int o=c[i]; 64 if(st[o].link!=-1){ 65 dp[st[o].link]+=(LL)st[st[o].link].len*cnt[o]*cnt[st[o].link]; 66 cnt[st[o].link]+=cnt[o]; 67 } 68 } 69 70 LL ans=(LL)n*(n+1)*(n-1)/2; 71 for(int i=0;i<sz;i++){ 72 ans-=2*dp[i]; 73 } 74 printf("%lld\n",ans); 75 76 return 0; 77 }