考虑用(AC)自动机来解决本题这样的多字符串匹配问题。
要最大化魔法分割后得到的禁忌串数目,最优情况肯定为在一个串中每个禁忌串的右端点进行分割。对应到(AC)自动机上,就是匹配到一个禁忌串后,就直接转移到根节点。
若用朴素的(DP)解决,发现题目中的(len)过大,于是用矩阵快速幂优化。
先构造初始矩阵,(a_{i,j})的值表示当串长为(1)时从状态(i)转移到状态(j)的概率,对这样的一个矩阵进行(len)次幂后,所得的含义即为串长为(len)时所对应的概率。
同时新增一个状态(t)来统计期望,若转移过程中,转移到了一个合法的状态,即匹配上了一个禁忌串,那么就可以把当前概率统计到状态(t)上了,最后直接查询根到状态(t)即可。
构造矩阵时,分情况讨论。设(P=frac{1}{alphabet}),若一个状态(x)可转移到状态(y),若状态(y)不是禁忌串的终止状态,则(a_{x,y})加上(P),否则让(a_{x,root})加上(P)和(a_{x,t})加上(P)。
具体实现细节看代码吧。
(code:)
#include<bits/stdc++.h>
#define maxn 110
using namespace std;
typedef long double ld;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,l,alph,tot,root;
ld P;
int trie[maxn][30],fail[maxn];
bool end[maxn];
char s[maxn];
struct matrix
{
ld a[maxn][maxn];
}m,e;
matrix operator *(const matrix &x,const matrix &y)
{
matrix z;
memset(z.a,0,sizeof(z.a));
for(int k=root;k<=tot+1;++k)
for(int i=root;i<=tot+1;++i)
for(int j=root;j<=tot+1;++j)
z.a[i][j]+=x.a[i][k]*y.a[k][j];
return z;
}
matrix qp(matrix x,int y)
{
matrix t=e;
while(y)
{
if(y&1) t=t*x;
x=x*x;
y>>=1;
}
return t;
}
void insert()
{
int len=strlen(s+1),p=root;
for(int i=1;i<=len;++i)
{
int ch=s[i]-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;
p=trie[p][ch];
}
end[p]=true;
}
void build()
{
queue<int> q;
for(int i=0;i<alph;++i)
if(trie[root][i])
q.push(trie[root][i]);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<alph;++i)
{
int y=trie[x][i];
if(y)
{
fail[y]=trie[fail[x]][i];
end[y]|=end[fail[y]],q.push(y);
}
else trie[x][i]=trie[fail[x]][i];
}
}
e.a[tot+1][tot+1]=m.a[tot+1][tot+1]=1;
for(int x=root;x<=tot;++x)
{
e.a[x][x]=1;
for(int ch=0;ch<alph;++ch)
{
int y=trie[x][ch];
if(end[y]) m.a[x][tot+1]+=P,m.a[x][root]+=P;
else m.a[x][y]+=P;
}
}
}
int main()
{
read(n),read(l),read(alph),P=(ld)1.0/(ld)alph;
for(int i=1;i<=n;++i)
scanf("%s",s+1),insert();
build(),m=qp(m,l);
printf("%Lf",m.a[root][tot+1]);
return 0;
}