题面
题解
首先显然是一个AC自动机上DP的题目。
接下来是套路:
令dp[p]dp[p]dp[p]从AC自动机上的ppp点走到结束节点的期望步数。
这里的结束节点指AC自动机上插入的若干个串的最后一个字符所在节点。
显然对于结束节点dp[p]=0dp[p]=0dp[p]=0。
对于其余节点,有转移方程dp[p]=(126∑i=126dp[ch[p][i]])+1dp[p]=(\frac{1}{26} \sum^{26}_{i=1} dp[ch[p][i]])+1dp[p]=(261∑i=126dp[ch[p][i]])+1。
然而,我们知道,AC自动机实际上是一个图,而且也并不是DAG。
注意到本题的数据规模较小,考虑使用高斯消元暴力解方程。
方程列为:
dp[p]=0dp[p]=0dp[p]=0(ppp为结束节点)
26dp[p]−∑i=126dp[ch[p][i]]=2626dp[p]-\sum^{26}_{i=1} dp[ch[p][i]]=2626dp[p]−∑i=126dp[ch[p][i]]=26(ppp为普通节点)
复杂度:O((n∣Si∣)3)O((n|S_i|)^3)O((n∣Si∣)3)
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAXN 250
#define CHSIZ 26
#define MOD 1000000007
#define clr(x) memset(x,0,sizeof(x))
int T,n;
char s[MAXN+5];
int fun[MAXN+5][MAXN+5],ans[MAXN+5],is_free[MAXN+5];
int fst_pow(int a,int b)
{
int res=1;
while(b){
if(b&1)res=(1LL*res*a)%MOD;
a=(1LL*a*a)%MOD;
b>>=1;
}
return res;
}
struct AC_automaton
{
int tr[MAXN+5][CHSIZ+1],endp[MAXN+5];
int fail[MAXN+5],pcnt;
void Init(){pcnt=0;memset(tr[0],0,sizeof(tr[0]));}
void Insert(char *s)
{
int len=strlen(s+1),p=0;
for(int i=1;i<=len;i++){
if(!tr[p][s[i]-'a'])
{
tr[p][s[i]-'a']=++pcnt;
fail[pcnt]=0;endp[pcnt]=0;
memset(tr[pcnt],0,sizeof(tr[pcnt]));
}
p=tr[p][s[i]-'a'];
}
endp[p]=1;
}
void Build()
{
queue<int> Q;
for(int i=0;i<CHSIZ;i++)
if(tr[0][i])Q.push(tr[0][i]);
while(!Q.empty())
{
int p=Q.front();
Q.pop();
endp[p]|=endp[fail[p]];
for(int i=0;i<CHSIZ;i++)
if(tr[p][i])
{
fail[tr[p][i]]=tr[fail[p]][i];
Q.push(tr[p][i]);
}
else tr[p][i]=tr[fail[p]][i];
}
}
void make_fun()
{
clr(fun),clr(ans),clr(is_free);
for(int i=0;i<=pcnt;i++)
{
if(endp[i])
fun[i][i]=1,fun[i][pcnt+1]=0;
else
{
fun[i][i]=26;
for(int j=0;j<CHSIZ;j++)
fun[i][tr[i][j]]--;
fun[i][pcnt+1]=26;
}
}
}
}ac;
int Gauss(int n,int m)
{
int col=0,k=0;
for(;k<m&&col<n;k++,col++)
{
int r=k;
for(int i=k+1;i<m;i++)
if(fabs(fun[r][col])<fabs(fun[i][col]))r=i;
if(!fun[r][col]){is_free[col]=1;k--;continue;}
if(r!=k)
for(int i=col;i<=n;i++)
swap(fun[k][i],fun[r][i]);
for(int i=k+1;i<m;i++)
if(fun[i][col])
{
int Div=(1LL*fun[i][col]*fst_pow(fun[k][col],MOD-2))%MOD;
for(int j=col;j<=n;j++)
fun[i][j]=(fun[i][j]-(1LL*fun[k][j]*Div)%MOD+MOD)%MOD;
fun[i][col]=0;
}
}
for(int i=k;i<m;i++)
if(fun[i][n]) return -1;
if(k<n)return n-k;
for(int i=n-1;i>=0;i--)
{
int tmp=fun[i][n];
for(int j=i+1;j<n;j++)
tmp=(tmp-(1LL*ans[j]*fun[i][j])%MOD+MOD)%MOD;
ans[i]=(1LL*tmp*fst_pow(fun[i][i],MOD-2))%MOD;
}
return 0;
}
int main()
{
//freopen("substring.in","r",stdin);
//freopen("substring.out","w",stdout);
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ac.Init();
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
ac.Insert(s);
}
ac.Build();
ac.make_fun();
Gauss(ac.pcnt+1,ac.pcnt+1);
printf("%d\n",(ans[0]+MOD)%MOD);
}
}