Link 设扔出$i$的概率为$P_i$。 设$f_{i,j}$为首次出现的是$s_i$且随机序列长度为$j$的概率,其OGF为$F_i(x)$。 设$g_i$表示随机序列长度达到$i$且尚未结束的概率,其OGF为$G(x)$。 设$a_{i,j,k}=[pre(s_i,k)=suf(s_j,k)],P(S)=prodlimits_{xin S}P_x$。 那么我们有
[
egin{aligned}
sumlimits_{i=1}^nF_i(x)+G(x)&=1+xG(x)\
forall iin[1,n]qquad G(x)P(s_i)x^{len_i}&=sumlimits_{j=1}^nsumlimits_{k=1}^{len_i}a_{i,j,k}F_j(x)P(s_i[k+1,len_i])x^{len_i-k}
end{aligned}
]
我们要求的是$sumlimits_^nF_i'(1)$,将上述等式变形可以得到
[
egin{aligned}
sumlimits_{i=1}^nF_i'(1)&=G(1)\
forall iin[1,n]qquad G(1)&=sumlimits_{j=1}^nF_j(1)sumlimits_{k=1}^{len_i}frac{a_{i,j,k}}{P(pre(s_i,k))}
end{aligned}
]
同时我们还有$sumlimits_nF_i(1)=1$。 然后利用Gauss消元法求解即可。 求$sumlimits_frac{a_{i,j,k}}{P(pre(s_i,k))}$可以使用hash、KMP、AC自动机等方法。
#include<cmath>
#include<cstdio>
#include<algorithm>
using ld=long double;
const int N=307;
int n,m,next[N];char s[N][N];ld a[N][N];
int main()
{
scanf("%d%d",&n,&m),a[n][n+1]=1,next[0]=-1;
for(int i=0;i<n;++i) scanf("%s",s[i]);
for(int i=0;i<n;++i)
{
for(int j=0,k=-1;j<m;next[++j]=++k) while(~k&&s[i][j]!=s[i][k]) k=next[k];
for(int j=0;j<n;++j)
{
int k=0;
for(int l=0;l<m;++l,++k) while(~k&&s[j][l]!=s[i][k]) k=next[k];
for(;k;k=next[k]) a[i][j]+=pow((ld)0.5,m-k);
}
a[i][n]=-pow((ld)0.5,m),a[n][i]=1;
}
for(int i=0;i<=n;++i)
{
int p=i;
for(int j=i+1;j<=n;++j) if(fabs(a[j][i])>fabs(a[p][i])) p=j;
std::swap(a[i],a[p]);
for(int j=n+i;j>=i;--j) a[i][j]/=a[i][i];
for(int j=0;j<=n;++j) if(i!=j) for(int k=n+1;k>=i;--k) a[j][k]-=a[j][i]*a[i][k];
}
for(int i=0;i<n;++i) printf("%.12Lf
",a[i][n+1]);
}