HAOI 2016 找相同字符

HAOI 2016 找相同字符

问题描述

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。

输入格式

两行,两个字符串 $s_1, s_2$​,长度分别为 $n_1, n_2$。
字符串中只有小写字母。

输出格式

输出一个整数表示答案

样例输入

aabb
bbaa

样例输出

10

提示

1≤n1,n2≤200000


中间加一个分隔符,把两个串拼接起来。对于这个大串建立后缀自动机。

对于两个原串共有的子串,对答案的贡献为“在s1中出现的次数$\times$在s2中出现的次数”,这显然可以用后缀自动机维护。对于后缀自动机上的节点$p$,其代表的字符串有$max[p]-max[par[p]]$个。

下面的代码给出了一种较方便的实现方式。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<stdio.h>
#include<algorithm>
#include<cstring>
#define MAXN 800005
#define ll long long
using namespace std;

char a[MAXN],b[MAXN];
int la,lb;
ll Ans;

int tot,mx[MAXN],par[MAXN],son[MAXN][27],sz1[MAXN],sz2[MAXN],las,rt;
int T[MAXN],P[MAXN];

int pusH(int val){mx[++tot]=val;return tot;}
void iniT(){rt=las=pusH(0);}
void inS(int t,int d){
int p,q,np,nq;
np=pusH(mx[las]+1);sz2[np]=d;sz1[np]=1;
for(p=las;p&&(!son[p][t]);p=par[p])son[p][t]=np;
if(!p)par[np]=rt;
else{
q=son[p][t];
if(mx[q]==mx[p]+1)par[np]=q;
else{
nq=pusH(mx[p]+1);
memcpy(son[nq],son[q],sizeof(son[q]));
par[nq]=par[q];
par[q]=par[np]=nq;
for(;p&&son[p][t]==q;p=par[p])son[p][t]=nq;
}
}
las=np;
}

void topsorT(){
int i,p;
for(i=1;i<=tot;i++)T[mx[i]]++;
for(i=1;i<=mx[las];i++)T[i]+=T[i-1];
for(i=tot;i;i--)P[T[mx[i]]--]=i;
for(i=tot;i;i--){
p=P[i];
sz1[par[p]]+=sz1[p];
sz2[par[p]]+=sz2[p];
}
}

int main(){
int i;
iniT();
scanf("%s%s",a,b);
la=strlen(a);lb=strlen(b);
for(i=0;i<la;i++)inS(a[i]-'a',0);
inS(26,0);
for(i=0;i<lb;i++)inS(b[i]-'a',1);
topsorT();
for(i=1;i<=tot;i++)Ans+=(ll)sz2[i]*(sz1[i]-sz2[i])*(mx[i]-mx[par[i]]);
printf("%lld\n",Ans);
}