传送门
也是广义 SAM 的板子题,建好广义 SAM,统计 (epA,epB,epC),然后对于区间 (ans[len[fa[x]]+1],...,ans[len[x]]) 加上 (epA imes epB imes epC) 就行了,当然这个用差分实现简单快捷。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=3e5+10;
const int mod=1e9+7;
int A[N*2],B[N*2],C[N*2];
LL ans[N];
int slen,n=mod;
char s[N];
struct SuffixAutoMachine{
int tot=1,len[N*2],fa[N*2],ch[N*2][26],*epos,a[N*2],c[N];
int newnode(int x){fa[++tot]=fa[x];len[tot]=len[x];memcpy(ch[tot],ch[x],sizeof(ch[tot]));return tot;}
int extend(int p,int c){
int q=ch[p][c],nq=newnode(q);
len[nq]=len[p]+1;fa[q]=nq;
for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
return nq;
}
int append(int p,int c){
if(ch[p][c]) if(len[ch[p][c]]==len[p]+1) return ch[p][c];else return extend(p,c);
int np=newnode(0);len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else if(len[ch[p][c]]==len[p]+1) fa[np]=ch[p][c];
else fa[np]=extend(p,c);
return np;
}
void insert(int *_epos){
int last=1;epos=_epos;
for(int i=1;i<=slen;i++) last=append(last,s[i]-'a'),epos[last]=1;
}
void solve(){
for(int i=1;i<=tot;i++) c[len[i]]++;
for(int i=1;i<N;i++) c[i]+=c[i-1];
for(int i=tot;i>=1;i--) a[c[len[i]]--]=i;
for(int i=tot;i>1;i--){
A[fa[a[i]]]+=A[a[i]];
B[fa[a[i]]]+=B[a[i]];
C[fa[a[i]]]+=C[a[i]];
}
for(int i=2;i<=tot;i++){
LL x=1ll*A[i]*B[i]%mod*C[i]%mod;
ans[len[fa[i]]+1]+=x;
ans[len[i]+1]-=x;
}
for(int i=1;i<=n;i++) ans[i]=((ans[i-1]+ans[i])%mod+mod)%mod;
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
}
}sam;
int main(){
scanf("%s",s+1),slen=strlen(s+1);
sam.insert(A);n=min(slen,n);
scanf("%s",s+1),slen=strlen(s+1);
sam.insert(B);n=min(slen,n);
scanf("%s",s+1),slen=strlen(s+1);
sam.insert(C);n=min(slen,n);
sam.solve();
return 0;
}
到这里,我后缀自动机的刷题基本结束了,虽然题单上还有很多难题没有刷,但那些都是结合了其他算法的题,对于 SAM 和广义 SAM 的原理和性质有了一些基本的了解就是这几天的主要任务了,也熟练的掌握了写法,效果还不错。之后也会慢慢把那些难题补完的。
下一步计划学习线段树合并。