Time Limit: 20 Sec Memory Limit: 256 MB
兩行,兩個字符串s1,s2,長度分別爲n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母php
輸出一個整數表示答案ios
aabb bbaa
10
第一眼看題,跟子串有關不是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的點出現位置靠後,具體見代碼
# 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); }