1. 程式人生 > >bzoj4566 / P3181 [HAOI2016]找相同字元

bzoj4566 / P3181 [HAOI2016]找相同字元

P3181 [HAOI2016]找相同字元

字尾自動機 (正解應是 廣義字尾自動機) 並不會廣義字尾自動機。 然鵝可以用普通的字尾自動機。   我們先引入一個問題:算出從一個串內取任意兩個不重合子串完全相同的方案數。 顯然,對於每個點$w$,$tot+=siz[w]*(siz[w]-1)/2*(len[w]-len[fa[w]])$ $siz[w]$表示該點對應子串出現次數 那麼答案即為$tot_{a+b}-tot_a-tot_b$ 計算$tot_{a+b}$時在$a,b$間插入一個特殊字元即可。(插入$'{'='z'+1$較方便) attention:陣列需要開n*2*2=800000大小!
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define N 800005
 6 int n; char s1[N],s2[N];
 7 long long ans,tot;
 8 struct Sam{
 9     int nxt[N][27],len[N],siz[N],fa[N];
10     int p,q,last,ed,a[N],c[N];
11 void clear(){ 12 last=ed=1; 13 memset(fa,0,sizeof(fa)); 14 memset(nxt,0,sizeof(nxt)); 15 memset(len,0,sizeof(len)); 16 memset(siz,0,sizeof(siz)); 17 memset(c,0,sizeof(c)); 18 } 19 void add(int c){ 20 p=last; len[last=++ed]=len[p]+1
; siz[ed]=1; 21 for(;p&&!nxt[p][c];p=fa[p]) nxt[p][c]=ed; 22 if(!p){fa[ed]=1; return;} 23 q=nxt[p][c]; 24 if(len[q]==len[p]+1){fa[ed]=q; return;} 25 len[++ed]=len[p]+1; 26 memcpy(nxt[ed],nxt[q],sizeof(nxt[q])); 27 fa[ed]=fa[q]; fa[q]=fa[ed-1]=ed; 28 for(;nxt[p][c]==q;p=fa[p]) nxt[p][c]=ed; 29 }//裸的板子 30 void calc(){ 31 for(int i=1;i<=ed;++i) ++c[len[i]]; 32 for(int i=1;i<=ed;++i) c[i]+=c[i-1]; 33 for(int i=1;i<=ed;++i) a[c[len[i]]--]=i;//對len進行排序代替dfs 34 for(int i=ed;i;--i){ 35 int w=a[i]; siz[fa[w]]+=siz[w]; 36 tot+=1ll*siz[w]*(siz[w]-1)/2*(len[w]-len[fa[w]]);//累計每個點的貢獻 37 } 38 } 39 }sam; 40 void solve(char *v,int x){ 41 tot=0; n=strlen(v+1); sam.clear(); 42 for(int i=1;i<=n;++i) sam.add(v[i]-'a'); 43 sam.calc(); ans+=tot*x; 44 } 45 int main(){ 46 scanf("%s",s1+1); scanf("%s",s2+1); 47 solve(s1,-1); solve(s2,-1); 48 strcat(s1+1,"{"); strcat(s1+1,s2+1); 49 solve(s1,1); printf("%lld",ans); 50 return 0; 51 }
View Code