[Bzoj4566][Haoi2016]找相同字符(廣義後綴自動機)

4566: [Haoi2016]找相同字符


 

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 861  Solved: 495
[Submit][Status][Discuss]

Description


 

給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不一樣當且僅當這兩
個子串中有一個位置不一樣。

 

Input


 

兩行,兩個字符串s1,s2,長度分別爲n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母php

 

Output


 

輸出一個整數表示答案ios

 

Sample Input


 

aabb bbaa

 

Sample Output


 

10

 

HINT

 

題解:


第一眼看題,跟子串有關不是Sam就是Sa (霧)數組

第二眼看題,不就是個廣義後綴自動機,邪魅一笑的發現這題很水啊spa

 

先來看看如何用sam求一個串的不一樣子串.net

建好sam後,一個結點所表明的子串是根節點到它的各個路徑,而後路徑數量恰好是dis[i] -dis[fa[i]];code

設sam裏有dt個結點,則一個串的不一樣子串爲∑(dis[i] - dis[fa[i]]), i ∈[1,dt]blog

根據sam的特色,一個點出現了,那麼必然它的父親也出現了,由於它的父親是它的後綴。排序

那麼對於每一次sam出來的點,咱們使它權值w[i]++,而後按dis基數排序後,倒着掃一遍w[fa[i]] += w[i];ip

那麼對於每一個子串,假設它對應結點爲i,那麼它出現次數爲 w[i]字符串

這樣咱們就利用sam的性質對於一個串求出了它不一樣子串數量,或者某個子串出現次數。

 

再來看看廣義後綴自動機。看看這篇博文http://blog.csdn.net/wangzhen_yu/article/details/45481269

看不懂不要緊,簡單解釋就是按一個串建完sam後,後面的每一個串的last回到根節點,又繼續建,不清空以前串的記錄。具體看代碼

而後對於每一個點,咱們就能夠發現,它能夠表明第一個字符串子串,也能夠表明第二個字符串子串。

 

因此咱們按一個串的方法,求出兩個字符串對應的w數組,那麼每一個點i就有w[i][0] 和 w[i][1];

這個點所表明的dis[fa[i]] - dis[i]個在第一個串的子串都出現了w[i][0] 次

這個點所表明的dis[fa[i]] - dis[i]個在第二個串的子串都出現了w[i][1] 次

那麼這個點所產生的貢獻就是 (dis[fa[i]] - dis[i]) * w[i][0] * w[i][1];

最後就發現答案是 ∑ (dis[fa[i]] - dis[i]) * w[i][0] * w[i][1]  , i ∈[1,dt]

 

須要注意的是由於用的是基數排序,相同dis的點相對前後順序是不變的,因此若是一次sam加了兩個點,要使表明當前last的點出現位置靠後,具體見代碼

 

AC代碼:


 

# include <cstdio> # include <cstring> # include <iostream>
using namespace std; typedef long long LL; const int N = 220012 << 2; int ch[N][26],fa[N],dis[N],dt = 1,que[N],l,ls,bac[N]; char str[N];LL ans,sz[N][2]; int Sam(int last,int c) { int u = last,np; while(u && !ch[u][c])que[++que[0]] = u,u = fa[u]; if(!u) { np = ++dt;dis[np] = dis[last] + 1;fa[np] = 1; while(que[0])ch[que[que[0]--]][c] = np; } else { int v = ch[u][c]; if(dis[v] == dis[u] + 1) { np = ++dt;dis[np] = dis[last] + 1;fa[np] = v; while(que[0])ch[que[que[0]--]][c] = np; } else { int av = ++dt;np = ++dt; dis[np] = dis[last] + 1; while(que[0])ch[que[que[0]--]][c] = np; dis[av] = dis[u] + 1; memcpy(ch[av],ch[v],sizeof ch[v]); fa[av] = fa[v];fa[v] = fa[np] = av; while(u && ch[u][c] == v)ch[u][c] = av,u = fa[u]; } } return np; } int main() { scanf("%s",str);l = strlen(str);ls = 1; for(int i = 0;i < l;i++)ls = Sam(ls,str[i] - 'a'),sz[ls][0]++; scanf("%s",str);l = strlen(str);ls = 1; for(int i = 0;i < l;i++)ls = Sam(ls,str[i] - 'a'),sz[ls][1]++; for(int i = 1;i <= dt;i++)bac[dis[i]]++; for(int i = 1;i < N;i++)bac[i] += bac[i - 1]; for(int i = dt;i >= 1;i--)que[--bac[dis[i]]] = i; for(int i = dt;i >= 1;i--)sz[fa[que[i]]][1] += sz[que[i]][1],sz[fa[que[i]]][0] += sz[que[i]][0]; for(int i = 1;i <= dt;i++)ans += 1LL * (dis[i] - dis[fa[i]]) * (sz[i][0] * sz[i][1]); printf("%lld\n",ans); }
相關文章
相關標籤/搜索