bzoj4566 / P3181 [HAOI2016]找相同字元
阿新 • • 發佈:2019-01-05
字尾自動機
(正解應是
廣義字尾自動機)
並不會廣義字尾自動機。
然鵝可以用普通的字尾自動機。
我們先引入一個問題:算出從一個串內取任意兩個不重合子串完全相同的方案數。
顯然,對於每個點$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];View Code11 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 }