1. 程式人生 > >[bzoj4566][SAM]找相同字元

[bzoj4566][SAM]找相同字元

Description

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

Input

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

Output

輸出一個整數表示答案

Sample Input

aabb

bbaa

Sample Output

10

題解

對這兩個串建SAM 記錄兩個right集合right[i][0]和right[i][1],表示第一個串,第二個串在這裡的right集合長度 每個點的貢獻就是

(tr[i].deptr[tr[i].parent].dep)right[i][0]right[i][1]

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
char ch1[210000],ch2[210000],ch[210000
]; struct SAM { int son[30],dep,parent; SAM(){memset(son,0,sizeof(son));} }tr[810000];int root,cnt,last,siz[810000][2]; void add(int x) { int np=++cnt,p=last; tr[np].dep=tr[p].dep+1; while(p&&!tr[p].son[x])tr[p].son[x]=np,p=tr[p].parent; if(p==0)tr[np].parent=root; else { int
q=tr[p].son[x]; if(tr[q].dep==tr[p].dep+1)tr[np].parent=q; else { int nq=++cnt; tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1; tr[q].parent=tr[np].parent=nq; while(p&&tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent; } } last=np; } int Rsort[810000],sa[810000]; int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); root=last=++cnt; scanf("%s",ch+1);int l1=strlen(ch+1); for(int i=1;i<=l1;i++)add(ch[i]-'a'+1),ch1[i]=ch[i]; last=root; scanf("%s",ch+1);int l2=strlen(ch+1); for(int i=1;i<=l2;i++)add(ch[i]-'a'+1),ch2[i]=ch[i]; for(int i=1;i<=cnt;i++)Rsort[tr[i].dep]++; for(int i=1;i<=l1+l2;i++)Rsort[i]+=Rsort[i-1]; for(int i=cnt;i>=1;i--)sa[Rsort[tr[i].dep]--]=i; for(int p=root,i=1;i<=l1;i++) { int y=ch1[i]-'a'+1;p=tr[p].son[y]; siz[p][0]++; } for(int p=root,i=1;i<=l2;i++) { int y=ch2[i]-'a'+1;p=tr[p].son[y]; siz[p][1]++; } for(int i=cnt;i>=1;i--)siz[tr[sa[i]].parent][0]+=siz[sa[i]][0],siz[tr[sa[i]].parent][1]+=siz[sa[i]][1]; LL ans=0; for(int i=1;i<=cnt;i++) ans+=(LL)(tr[i].dep-tr[tr[i].parent].dep)*siz[i][0]*siz[i][1]; printf("%lld\n",ans); return 0; }