​题目传送门 - BZOJ2553​
题意概括

  引用一下lych大佬的:

  在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。


题解

  我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。

  然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。

  我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。

  然后我们考虑原问。

  对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?

  我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。

  然后回到矩阵中。

  假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1

  那么我们可以把它记入答案中。

  同理,我们可以在矩阵中多开一列,用来维护答案。

 


代码



#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long double LD;
const int S=80;
struct Trie{
int fail,e,Next[26];
void clear(){
fail=e=0;
memset(Next,0,sizeof Next);
}
}tree[S];
int n,len,alpha,cnt;
void build(char ch[]){
int rt=1,t,len=strlen(ch);
for (int i=0;i<len;i++){
t=ch[i]-'a';
if (!tree[rt].Next[t]){
tree[++cnt].clear();
tree[rt].Next[t]=cnt;
}
rt=tree[rt].Next[t];
}
tree[rt].e=1;
}
void build_AC(){
int q[S],head=0,tail=0;
int rt,son,k;
q[++tail]=1,tree[0].fail=1;
while (head<tail){
int rt=q[++head];
for (int i=0;i<alpha;i++){
son=tree[rt].Next[i];
if (!son){
tree[rt].Next[i]=tree[tree[rt].fail].Next[i];
continue;
}
k=tree[rt].fail;
while (!tree[k].Next[i])
k=tree[k].fail;
tree[son].fail=tree[k].Next[i];
tree[son].e|=tree[tree[k].Next[i]].e;
q[++tail]=son;
}
}
}
int m;
struct Mat{
LD v[S][S];
Mat (){}
Mat (int x){set(x);}
void print(){
for (int i=1;i<=m;i++,puts(""))
for (int j=1;j<=m;j++)
printf("%5.3Lf ",v[i][j]);
puts("");
}
void set(int x){
memset(v,0,sizeof v);
if (x==1)
for (int i=1;i<=m;i++)
v[i][i]=1;
}
Mat operator * (Mat b){
Mat ans(0);
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=m;k++)
ans.v[i][j]+=v[i][k]*b.v[k][j];
return ans;
}
Mat operator ^ (int y){
Mat ans(1),x=*this;
while (y){
if (y&1)
ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
}M(0);
char s[S];
int main(){
scanf("%d%d%d",&n,&len,&alpha);
cnt=1;
tree[0].clear();
tree[1].clear();
for (int i=0;i<alpha;i++)
tree[0].Next[i]=1;
for (int i=1;i<=n;i++){
scanf("%s",s);
build(s);
}
build_AC();
m=cnt+1;
LD addv=1.0/(1.0*alpha);
for (int i=1;i<=cnt;i++)
for (int j=0;j<alpha;j++)
if (tree[tree[i].Next[j]].e)
M.v[i][1]+=addv,M.v[i][m]+=addv;
else
M.v[i][tree[i].Next[j]]+=addv;
M.v[m][m]=1;
Mat Mans=M^len;
printf("%.10lf",(double)Mans.v[1][m]);
return 0;
}