http://www.elijahqi.win/archives/3036
Description
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
Input
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
Sample Output
10
HINT
Source
感谢sbw巨佬指点orz
首先将A串建出SAM然后将B串在A串上跑 先求出A串每个状态的right集合的大小 然后如果匹配到这个点就用(l-len[fa[x]])*size[x] 表示B的每一个前缀在A中的匹配方案数 那么光这样是不行的我们少计算了这样一个前缀的所有后缀在A中的匹配方案数所以我设e表示每个字串在A中状态出现的次数即计算时候的系数 sub表示这个节点的这种状态会被多少个B用上 然后又因为这个节点表示len[fa[p]]-len[p]这么多子串且出现次数为size[p]
sub[x]size[x](len[x]-len[fa[x]])
#include<cstdio>
#include<cctype>
#include<cstring>
#define ll long long
#include<algorithm>
using namespace std;
const int N=200020;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
int cnt=1,last=1,root=1,ch[N<<1][26],len[N<<1],fa[N<<1],size[N<<1];
int C[N],rk[N<<1],e[N<<1],sub[N<<1];ll ans;
inline void insert1(int x){static int p,np,q,nq;
np=++cnt,p=last;len[np]=len[p]+1;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=root;else{
q=ch[p][x];if (len[p]+1==len[q]) fa[np]=q;else{
nq=++cnt;len[nq]=len[p]+1;fa[nq]=fa[q];memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[q]=fa[np]=nq;for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}size[np]=1;last=np;
}
int main(){
// freopen("bzoj4566.in","r",stdin);
char c=gc();while(c<'a'||c>'z') c=gc();int le=0;
while(c>='a'&&c<='z') insert1(c-'a'),c=gc(),++le;
for (int i=1;i<=cnt;++i) ++C[len[i]];
for (int i=1;i<=le;++i) C[i]+=C[i-1];
for (int i=1;i<=cnt;++i) rk[C[len[i]]--]=i;
for (int i=cnt;i;--i){
static int x;x=rk[i];
size[fa[x]]+=size[x];
}c=gc();while(c<'a'||c>'z') c=gc();int p=1,l=0;
while(c>='a'&&c<='z'){static int x;x=c-'a';
if (ch[p][x]) {p=ch[p][x];++l;}else{
for (;p&&!ch[p][x];p=fa[p]);
if (!p) {p=1;l=0;}else l=len[p]+1,p=ch[p][x];
}c=gc();ans+=(ll)(l-len[fa[p]])*size[p];++e[p];
}
for (int i=cnt;i>=2;--i){
static int x;x=rk[i];
sub[fa[x]]+=sub[x]+e[x];
}
for (int i=2;i<=cnt;++i){
static int x;x=rk[i];
ans+=(ll)sub[x]*size[x]*(len[x]-len[fa[x]]);
}printf("%lld\n",ans);
return 0;
}