Description
周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。大家纷纷觉得这个游戏非常符
合同学们的特色,但只是扔硬币实在是太单调了。同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币
,其他同学记录下正反面情况。用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比
如HTT表示第一次正面朝上,后两次反面朝上。但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个
长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只
有一个同学胜利,同学们猜的n个序列两两不同。很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节
。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。
这题真是妙啊。
我们首先设第i个同学赢的概率是p[i], 再假设存在一个很长的串 S , 它没有和任何串匹配,并且记它出现的概率为p[n+1](虽然它很长但是生成它也是有概率的)。
那么我们现在就相当于给S后面接一些字符,判断它是否能匹配上某个串。 接着我们考虑, 假如当前想要匹配的串的某个前缀是另外一个待匹配串的后缀,由于S的最后几项是未知的,那么有可能先让另外一个串匹配走了。 举个网上最常见的例子,假如当前要匹配的串A是TTH,而另外有一个串B是HTT,那么从S后面直接接了一个TTH的概率就是1/(2^3),然而这个概率不仅仅包括了TTH匹配了的情况,还包括了可能S的末尾是H,加TT把B已经匹配好了剩下一位任选匹配上的概率,还包括了可能S的末尾是HT,加T也把B匹配好了的剩下两位任选匹配上的概率,即 1/8 * p[n+1] =p[A] + 1/2 p[B] + 1/4 p[B] + ... 省略号为其他与A有冲突的串 。即我们可以得到 0=-1/8 * p[n+1] +p[A] + 1/2 p[B] + 1/4 p[B] + ... 的方程组。n+1个变量,现在已经有了n个方程,再因为必须有一个赢,所有有p[1]-p[n]的和为1,这样n+1个变量n+1个方程就可以求解了。
注意这题的0.5^x不要预处理,用math库的,反正我一直我调了一个小时。
下附AC代码。
// luogu-judger-enable-o2
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define maxn 305
#define double long double
using namespace std;
const double eps=1e-8;
int n,m,len;
int fail[maxn+maxn];
double mypow[maxn];
char s[maxn][maxn];
char t[maxn+maxn];
double a[maxn][maxn];
void kmp()
{
int i=0,j=-1; fail[0]=-1;
while(i<=m+m)
{
// cerr<<"its "<<i<<endl;
if(j==-1 || t[i]==t[j])
i++,j++,fail[i]=j;
else
j=fail[j];
}
}
double myabs(double now)
{
return now>0.0 ? now : -now;
}
double gauss()
{
for(int i=1;i<=n;i++)
{
int row=i;
for(int j=i+1;j<=n;j++)
if(myabs(a[j][i])>myabs(a[row][i]))
row=j;
if(row!=i)
for(int j=1;j<=n+1;j++)
swap(a[i][j],a[row][j]);
for(int j=i+1;j<=n;j++)
{
double temp=a[j][i]/a[i][i];
for(int k=i;k<=n+1;k++)
a[j][k]-=temp*a[i][k];
}
}
for(int i=n;i>=1;i--)
{
double ans=a[i][n+1]/a[i][i];
a[i][n+1]=ans;
for(int j=i-1;j>=1;j--)
{
a[j][n+1]-=ans*a[j][i];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
mypow[0]=1.0;
for(int i=1;i<=n;i++)
mypow[i]=0.5*mypow[i-1];
// cerr<<"+1"<<endl;
for(int i=1;i<=n;i++)
{
len=0;
for(int j=1;j<=m;j++) t[len++]=s[i][j]; t[len++]='$';
// a[i][i]=1.0;
a[i][n+1]=-pow(0.5,m);
for(int j=1;j<=n;j++)
// if(i!=j)
{
len=m+1;
for(int k=1;k<=m;k++) t[len++]=s[j][k];
// for(int k=0;k<len;k++)
// cout<<t[k]<<" ";
// cout<<endl;
kmp();
int temp=fail[len];
// for(int k=1;k<=len;k++)
// cout<<t[k];
// cout<<endl;
// for(int k=1;k<=len;k++)
// cout<<fail[k]<<endl;
while(temp>=1)
{
a[i][j]+=pow(0.5,m-temp);
temp=fail[temp];
}
}
}
// cerr<<"+1"<<endl;
for(int i=1;i<=n;i++)
a[n+1][i]=1.0;
a[n+1][n+2]=1.0;
n++;
gauss();
for(int i=1;i<=n-1;i++)
printf("%.10Lf\n",a[i][n+1]);
}