如果只有一个串,那么建立 S A M SAM SAM直接求本质不同的子串个数就好了
可以选择利用
p
a
r
e
n
t
parent
parent树性质求,也可以选择在DAG
上
d
p
dp
dp不同路径的方案数
现在有多个串,不好利用 S A M SAM SAM的形式:每条路径代表的子串不同(可自动去重)
考虑对每个串都建立了一个
S
A
M
SAM
SAM,每个串都有一个DAG
设串
A
A
A有子串a,ab,abc,空
,串
B
B
B有子串c,空
那么 a b + c ab+c ab+c也是 a b c abc abc,而 a b c + 空 abc+空 abc+空也是 a b c abc abc,如何去重??
我们规定,如果串 A A A中走到节点 k k k有到字母 c c c的转移,那我们就在本串转移
不跳转到下一个串去。因为如果跳过去,就和在本串转移形成重复。
那么做法就呼之欲出了
如果一个节点
k
k
k没有到字母
c
c
c的边,让节点
k
k
k指向下一个串DAG
根节点下的字母
c
c
c
这样形成的,把 n n n张 D A G DAG DAG合并成一张 D A G DAG DAG的图
在上面统计路径条数就好了
这个DAG
无敌。
只有确认了在当前串不会产生重复,才会转移到下一个串
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e6+10;
const int mod = 1e9+7;
int n,f[maxn];
int zi[maxn][27],fa[maxn],len[maxn],id,las,rt[maxn];
char a[maxn];
void insert(int c,int r)
{
int p = las, np = ++id; las = id;
len[np] = len[p]+1;
for( ; p&&zi[p][c]==0 ; p=fa[p] ) zi[p][c] = np;
if( p==0 ) fa[np] = rt[r];
else
{
int q = zi[p][c];
if( len[q]==len[p]+1 ) fa[np] = q;
else
{
int nq = ++id;
memcpy( zi[nq],zi[q],sizeof zi[nq] );
fa[nq] = fa[q], len[nq] = len[p]+1;
fa[np] = fa[q] = nq;
for( ; p&&zi[p][c]==q ; p = fa[p] ) zi[p][c] = nq;
}
}
}
int dfs(int u)
{
if( f[u] ) return f[u];
f[u] = 1;
for(int i=0;i<=25;i++)
if( zi[u][i] ) f[u] = ( 1ll*f[u]+dfs( zi[u][i] ) )%mod;
return f[u];
}
signed main()
{
cin >> n;
for(int i=1;i<=n;i++)
{
scanf("%s",a+1 );
rt[i] = ++id, las = id;//新的SAM图了
int le = strlen( a+1 );
for(int j=1;j<=le;j++) insert( a[j]-'a',i );
}
for(int i=n-1;i>=1;i--)
{
for(int j=rt[i];j<rt[i+1];j++)//枚举第i个SAM的每一个节点
for(int k=0;k<=25;k++)//枚举每个字母的出边
if( zi[j][k]==0 ) zi[j][k] = zi[rt[i+1]][k];
}
cout << dfs(1);//从第一个SAM的DAG开始DP
}