首先我们把这些串扔到AC自动机上。。。
可以发现要匹配尽可能多的子串,我们只要贪心地在AC自动机上匹配即可
所以建完trie树后需要删去是某个模版串后缀的所有模版串
然后我们根据AC自动机上的状态,可以得出从第i位转移到第i+1位时,原状态等价于自动机上第j个状态,现状态等价于自动机上第k个状态的概率的转移方程:
f[i+1][k]=f[i][j]/alphabet
(在这之前先把fail指针的信息整合到trie的子节点关系上即可)
特别地,当新状态为模版串末位时,我们直接转移到trie树的根节点,并累加伤害的期望值
因为状态有限,且只能由第i位转移到第i+1位。。
我们就可以用矩阵快速幂轻松地优化了!(总结点数还是比较和谐的)
AC Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define N 110
#define maxsiz 101
using namespace std;
struct matrix {
int l,r;
long double m[N][N];
}ori,tmp,f;
int ch[N][26],n,len,alphabet,fail[N],q[N],m,cnt;
char s[N];
bool boo[N];
void build_trie(int id){
int now=0;
for (int i=1;i<=m;i++) {
if (!ch[now][s[i]-'a']) ch[now][s[i]-'a']=++cnt;
now=ch[now][s[i]-'a'];
if (boo[now]) break;
}
boo[now]=1;
}
void getfail(){
int h=0,t=1;q[h]=0;
while (h!=t) {
int now=q[h++];if (h>maxsiz)h=0;
int tmp=0;
for (int i=0;i<alphabet;i++) if (ch[now][i]){
int x=ch[now][i];int y=fail[now];
while (y&&(!ch[y][i])) y=fail[y];
if (ch[y][i]!=x) fail[x]=ch[y][i];
else fail[x]=0;
q[t++]=x;if (t>maxsiz) t=0;
}
else ch[now][i]=ch[fail[now]][i];
}
ori.m[cnt+1][cnt+1]=1;
}
void mul(matrix &x,matrix &y){
tmp.l=x.l;tmp.r=y.r;
memset(tmp.m,0,sizeof(tmp.m));
for (int k=0;k<x.r;k++)
for (int i=0;i<x.l;i++)
for (int j=0;j<y.r;j++)
tmp.m[i][j]+=x.m[i][k]*y.m[k][j];
x=tmp;
return ;
}
void build_matrix(){
long double xx=(long double)1/alphabet;
for (int i=0;i<=cnt;i++) if (!boo[i])
for (int j=0;j<alphabet;j++)
if (boo[ch[i][j]]) ori.m[i][cnt+1]+=xx,ori.m[i][0]+=xx;
else ori.m[i][ch[i][j]]+=xx;
ori.m[cnt+1][cnt+1]=1;
}
void dfs(int now){
if (boo[now]) for (int i=0;i<alphabet;i++) ch[now][i]=0;
else for (int i=0;i<alphabet;i++) if (ch[now][i]) dfs(ch[now][i]);
}
int main(){
scanf("%d%d%d",&n,&len,&alphabet);
for (int i=1;i<=n;i++){
scanf("%s",s+1);
m=strlen(s+1);
build_trie(i);
}
dfs(0);
ori.l=ori.r=cnt+2;
f.l=1;f.r=cnt+2;
f.m[0][0]=1;
getfail();
build_matrix();
for (;len;len>>=1,mul(ori,ori)) if (len&1) mul(f,ori);
double ans=f.m[0][cnt+1];
printf("%.9lf",ans);
}