周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。
大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。
同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。
用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。
但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的n个序列两两不同。
很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。
n , m < = 300 n,m<=300 n,m<=300
KMP入门教程:传送门
令
p
i
p_i
pi表示第
i
i
i个同学胜利的概率。
假设第
i
i
i个同学猜的第
j
j
j次是
a
i
,
j
a_{i,j}
ai,j,现在有一种不合法的状态
S
S
S。
如果
S
S
S后面摇出的依次是
a
i
,
1
,
a
i
,
2
,
a
i
,
3
,
.
.
.
,
a
i
,
m
a_{i,1},a_{i,2},a_{i,3},...,a_{i,m}
ai,1,ai,2,ai,3,...,ai,m,那么游戏一定会结束(但不一定是第
i
i
i个同学胜利,因为S的后缀与
a
i
a_i
ai的前缀可能组成其他同学猜的串)。
如果第
j
j
j个串的后
k
k
k位与第
i
i
i个串前
k
k
k位相同,那么有
1
2
m
−
k
\Large\frac{1}{2^{m-k}}
2m−k1的概率使第
j
j
j个同学胜利。
令
H
=
Σ
j
=
1
n
p
j
∗
1
2
m
−
k
H=\Large\Sigma\large_{j=1}^np_j*\frac{1}{2^{m-k}}
H=Σj=1npj∗2m−k1,则珂以列出
i
i
i个方程。
又因为
p
1
+
p
2
+
.
.
.
+
p
n
=
1
p_1+p_2+...+p_n=1
p1+p2+...+pn=1,所以这是一个
n
+
1
n+1
n+1元一次方程组。
所以用kmp求出所有
k
k
k,用高斯消元求解
p
p
p数组即珂。
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=305;
int n,m,nxt[Size][Size];
char str[Size][Size];
void GetNext(int x) {
for(re i=2,j=0; i<=m; i++) {
while(j>0 && str[x][j+1]!=str[x][i]) j=nxt[x][j];
if(str[x][j+1]==str[x][i]) j++;
nxt[x][i]=j;
}
}
double pow2[Size];
double Compare(int x,int y) {
int j=0;
for(re i=1; i<=m; i++) {
while(j>0 && str[x][j+1]!=str[y][i]) j=nxt[x][j];
if(str[x][j+1]==str[y][i]) j++;
}
double ans=0;
while(j) {
ans+=pow2[m-j];
j=nxt[x][j];
}
return ans;
}
int same[Size];
double f[Size][Size];
double x[Size];
void Gauss() {
int siz=n+2;
for(re i=1; i<=n+1; i++) {
for(re j=1; j<=n+1; j++) {
if(i==j) continue;
// if(fabs(f[i][i])<=1e-12) continue;
double k=f[j][i]/f[i][i];
for(re l=1; l<=siz; l++) {
f[j][l]-=k*f[i][l];
}
}
}
for(re i=1; i<=n+1; i++) {
x[i]=-f[i][n+2]/f[i][i];
}
// printf("I love Chtholly forever");
}
int main() {
// freopen("game5.in","r",stdin);
pow2[0]=1;
for(re i=1; i<=300; i++) {
pow2[i]=pow2[i-1]*0.5;
}
n=read();
m=read();
for(re i=1; i<=n; i++) {
scanf("%s",str[i]+1);
GetNext(i);
}
for(re i=1; i<=n; i++) {
for(re j=1; j<=n; j++) {
// int len=Compare(j,i);
// f[i][j]=pow2[m-len];
f[i][j]=Compare(i,j);
}
f[i][n+1]=-pow2[m]; //-H
f[i][n+2]=0;
}
for(re i=1; i<=n; i++) { //p1+p2+...+pn-1=0
f[n+1][i]=1;
}
f[n+1][n+2]=-1;
Gauss();
for(re i=1; i<=n; i++) {
printf("%.10lf\n",x[i]);
}
return 0;
}