BZOJ4566 [Haoi2016]找相同字符 字符串 SAM

时间:2023-03-08 16:49:32

原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4566.html

题目传送门 - BZOJ4566

题意

  给定两个字符串 $s1$ 和 $s2$ ,问有多少 $a,b,c,d$ 满足 $s1[a\cdots b] = s2[c\cdots d]$ 。

  $|s1|,|s2|\leq 200000$

题解

  建个广义 SAM ,然后统计一下。

  模板题。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200005*4;
struct SAM{
int Next[26],fa,Max;
}t[N];
int size,v[N][2],in[N],q[N],head,tail;
char s[N];
void init(){
memset(t,0,sizeof t);
memset(v,0,sizeof v);
size=1,t[0].Max=-1;
for (int i=0;i<26;i++)
t[0].Next[i]=1;
}
int extend(int p,int c){
if (t[p].Next[c]&&t[p].Max+1==t[t[p].Next[c]].Max)
return t[p].Next[c];
int np=++size,q,nq;
t[np].Max=t[p].Max+1;
for (;!t[p].Next[c];p=t[p].fa)
t[p].Next[c]=np;
q=t[p].Next[c];
if (t[p].Max+1==t[q].Max)
t[np].fa=q;
else {
nq=++size;
t[nq]=t[q],t[nq].Max=t[p].Max+1;
t[np].fa=t[q].fa=nq;
for (;t[p].Next[c]==q;p=t[p].fa)
t[p].Next[c]=nq;
}
return np;
}
void build(int x){
scanf("%s",s+1);
int n=strlen(s+1);
for (int i=1,p=1;i<=n;i++)
v[p=extend(p,s[i]-'a')][x]++;
}
int main(){
init();
build(0);
build(1);
memset(in,0,sizeof in);
for (int i=2;i<=size;i++)
in[t[i].fa]++;
head=tail=0;
for (int i=1;i<=size;i++)
if (in[i]==0)
q[++tail]=i;
while (head<tail){
int x=q[++head];
for (int i=0;i<2;i++)
v[t[x].fa][i]+=v[x][i];
if ((--in[t[x].fa])==0)
q[++tail]=t[x].fa;
}
LL ans=0;
for (int i=2;i<=size;i++)
ans+=1LL*v[i][0]*v[i][1]*(t[i].Max-t[t[i].fa].Max);
printf("%lld",ans);
return 0;
}