【JZOJ5129】字符串

Description

在这里插入图片描述

Solution

对每个串建SAM,要求本质不同的个数,即是从前面的串跑完跑不出来才跳到最近的后一个串上跑。
具体来说,对于S这个自动机一个节点的出边c,如果它不存在,连向下一个源点(right集为满)有这条出边的SAM上。连完后对整个DAG做一下拓扑序dp。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j,o=k;i<=o;++i)
#define fd(i,j,k) for(int i=j,o=k;i>=o;--i)
using namespace std;
const int N=2e6+2e5,mo=1e9+7;
int tr[N][26],fa[N],ln[N];
int la,tot;
int st[N],now;
int turn(char ch) {return ch-'a';}
void extend(int now,int c){
	int p=la,np=++tot;
	ln[np]=ln[p]+1,la=np;
	for(;p && !tr[p][c];p=fa[p]) tr[p][c]=np;
	if(!p) return void(fa[np]=st[now]);
	int q=tr[p][c];
	if(ln[p]+1==ln[q]) fa[np]=q;
	else{
		int nq=++tot;
		fo(i,0,25) tr[nq][i]=tr[q][i];
		fa[nq]=fa[q];
		fa[q]=fa[np]=nq,ln[nq]=ln[p]+1;
		for(;p && tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
	}
}
char s[N];
int an[N];
int ne[26],rd[N],d[N];
int f[N],n,ans=0;
void inc(int &x,int y){
	x=x+y>=mo?x+y-mo:x+y;
}
void pre(){
	fo(i,1,tot)
	fo(j,0,25) if(tr[i][j]) ++rd[tr[i][j]];
	int l=0,r=0;
	fo(i,1,n) d[++r]=st[i];
	f[st[1]]=1;
	for(;l<r;){
		int x=d[++l];
		fo(i,0,25){
			int v=tr[x][i];
			if(!v) continue;
			inc(f[v],f[x]);
			if(!(--rd[v])) d[++r]=v;
		}
	}
	fo(i,1,tot) inc(ans,f[i]);
}
int main()
{
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	scanf("%d",&n);
	fo(i,1,n){
		scanf("%s",s+1);
		int l=strlen(s+1);
		st[i]=la=++tot;
		fo(j,1,l) extend(i,turn(s[j]));
	}
	st[n+1]=tot+1;
	fd(i,n,1){
		fo(p,st[i],st[i+1]-1)
		fo(c,0,25) if(!tr[p][c]) tr[p][c]=ne[c];
		fo(c,0,25) ne[c]=tr[st[i]][c];
	}
	pre();
	printf("%d",ans);
}
posted @ 2019-03-19 21:33  sadstone  阅读(39)  评论(0编辑  收藏  举报