题意
给出n个串,定义一个长度为len的字符串的权值为:所有拆分方案中,最大的完全等于这n个串中一个的部分的数量。求所有长度为len的字符串的权值期望。
n<=5,len<=10^9,每个字符串长度<=15。
分析
显然可以上AC自动机。
一开始的思路是设f[i,j]表示走了i步走到节点j的期望权值,然后建出状态转移矩阵后发现无法转移。搜了题解才发现可以设f[i,j]表示走了i步走到节点j的概率,然后多开一维计数器来计算期望。然后就可以直接转移了。
这告诉我们以后做期望题的时候可以考虑先求概率。
要开long double。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=80;
int n,m,len,ch[N][26],sz,val[N],fail[N];
char str[20];
queue<int> que;
struct Matrix
{
long double a[N][N];
void clear(int n)
{
for (int i=0;i<=n;i++)
for (int j=0;j<=n;j++)
a[i][j]=0;
}
void unit(int n)
{
clear(n);
for (int i=0;i<=n;i++) a[i][i]=1;
}
}a;
void ins()
{
int len=strlen(str),now=0;
for (int i=0;i<len;i++)
if (!ch[now][str[i]-'a']) now=ch[now][str[i]-'a']=++sz;
else now=ch[now][str[i]-'a'];
val[now]=1;
}
void get_fail()
{
for (int i=0;i<m;i++) if (ch[0][i]) que.push(ch[0][i]);
while (!que.empty())
{
int u=que.front();que.pop();
for (int i=0;i<m;i++)
if (ch[u][i]) fail[ch[u][i]]=ch[fail[u]][i],que.push(ch[u][i]);
else ch[u][i]=ch[fail[u]][i];
}
long double w=(long double)1/m;sz++;
for (int i=0;i<sz;i++)
{
if (val[i]) continue;
for (int j=0;j<m;j++)
if (!val[ch[i][j]]) a.a[i][ch[i][j]]+=w;
else a.a[i][0]+=w,a.a[i][sz]+=w;
}
a.a[sz][sz]=1;
}
double ksm(double x,int y)
{
double ans=1;
while (y)
{
if (y&1) ans*=x;
x*=x;y>>=1;
}
return ans;
}
void mul(Matrix &c,Matrix a,Matrix b)
{
c.clear(sz);
for (int i=0;i<=sz;i++)
for (int k=0;k<=sz;k++)
for (int j=0;j<=sz;j++)
c.a[i][j]+=a.a[i][k]*b.a[k][j];
}
Matrix Matrix_ksm(Matrix x,int y)
{
Matrix ans;ans.unit(sz);
while (y)
{
if (y&1) mul(ans,ans,x);
mul(x,x,x);y>>=1;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&len,&m);
for (int i=1;i<=n;i++) scanf("%s",str),ins();
get_fail();
a=Matrix_ksm(a,len);
printf("%.10lf",(double)a.a[0][sz]);
return 0;
}