有一种暴力方式:对所有猜好的的序列建一个AC自动机,然后对这个有向图解方程求期望经过次数。
(然而好像没什么用)
我们常试着少用一些中间状态去转移的期望。
设为胜利的期望。
定义为不满足任何一种序列的情况,现在有两个同学进行了猜测,设序列分别为
为方便解释,这里给出一个例子。
设
把所有往后延伸这个串一定能结束,所有胜利的情况也可以由某个加上组成。
现在有一个问题:并不是所有加上都是胜利的情况。
这里先碰到底。
(的后缀加上的前缀构成了)
这里会发现,任何串的某一后缀如果与的某一前缀相同,都会有可能把的胜利从中途劫走。
那些后缀与的前缀 相同的,他们胜利的情况可以用表示出来,但这里我们把定义成与相同的前缀部分。
这里的对所有都做了一次增加,所以对于,它所有可能的情况都能卡掉。
所以在计算的时候,需要减去那些出现了后缀的那些字符串的期望。
设的前缀与的后缀的公共部分长度为。
这里如果统计时出现的某种前缀卡掉了,那卡掉之后必须沿着添加的字符继续添加下去,否则不会被计入的统计。
所以实际上减去的是加上与不同的那一段,而每走一步期望都要减少到原来的一半。
这样要减去的期望就是的期望的
一个很有趣的事情是:要在统计里排除的情况只需要考虑某个字符串的后缀,前缀会由来解决。
这样的话一个可能减去很多次。
现在把AC自动机中非结束节点的期望到达次数之和定为(其实就是的期望啦)
这样对于每个都有方程:
(这里要乘是因为我们钦定所有按照序列走步)
最后加上就可以凑齐个式子高斯消元了。
这里实现的时候有一些问题要注意一下:
- 最好把它当作一个变量看,要不然会出现爆精度导致系数全0得到未知数方程
- GCC在处理超过字长的位移时直接把位移膜了字长,而不是变成0,所以对于超过字长的位移需要特判。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
const int N=302;
const long double eps=1e-12;
int n,m;
char ch[N][N];
long double mp[N][N];
inline int nxi(){
int x=0;
char c;
while((c=getchar())>'9'||c<'0');
while(x=x*10-48+c,(c=getchar())>='0'&&c<='9');
return x;
}
inline void get_mp(int t){
static int nx[N];
for(int j=0,i=2;i<=m;++i){
while(j&&ch[t][i]!=ch[t][j+1]) j=nx[j];
if(ch[t][i]==ch[t][j+1]) ++j;
nx[i]=j;
}
mp[t][t]=-1;
for(int i=1;i<=n;++i){
int j=1,k=0;
for(;j<=m;++j){
while(k&&ch[i][j]!=ch[t][k+1]) k=nx[k];
if(ch[i][j]==ch[t][k+1]) ++k;
}
if(k==m) k=nx[k];
for(;k;k=nx[k]){
if(m-k>63) break;
mp[t][i]-=1.0/(1ull<<(m-k));
}
}
}
inline void gauss(){
for(int i=1;i<=n+1;++i){
int j=i;
while(j<=n+1&&fabs(mp[j][i])<eps) ++j;
if(j>n+1) return;
std::swap(mp[i],mp[j]);
for(int k=1;k<=n+1;++k){
if(k==i) continue;
const long double p=mp[k][i]/mp[i][i];
for(int l=0;l<=n+1;++l){
mp[k][l]-=mp[i][l]*p;
}
}
}
for(int i=1;i<=n+1;++i){
if(fabs(mp[i][0])>eps) mp[i][i]=mp[i][0]/mp[i][i];
else mp[i][i]=0;
}
}
inline void gauss_init(){
for(int i=1;i<=n;++i){
mp[n+1][i]=1;
get_mp(i);
mp[i][n+1]=1;
//we don't need to know men.
//1/(2^m)*men instead of men is also ok.
}
mp[n+1][0]=1;
}
int main(){
#ifndef ONLINE_JUDGE
// freopen("a.in","r",stdin);
#endif
n=nxi(),m=nxi();
for(int i=1;i<=n;++i){
scanf("%s",ch[i]+1);
}
gauss_init();
gauss();
for(int i=1;i<=n;++i){
printf("%.10lf\n",(double)mp[i][i]);
}
return 0;
}