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);
}