CF452E Three strings【广义SAM】

传送门
也是广义 SAM 的板子题,建好广义 SAM,统计 \(epA,epB,epC\),然后对于区间 \(ans[len[fa[x]]+1],...,ans[len[x]]\) 加上 \(epA\times epB\times 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 的原理和性质有了一些基本的了解就是这几天的主要任务了,也熟练的掌握了写法,效果还不错。之后也会慢慢把那些难题补完的。
下一步计划学习线段树合并。

猜你喜欢

转载自www.cnblogs.com/BakaCirno/p/12670860.html