周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。
大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。
同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。
用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。
但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的n个序列两两不同。
很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。
KMP入门教程:传送门
令表示第个同学胜利的概率。
假设第个同学猜的第次是,现在有一种不合法的状态。
如果后面摇出的依次是,那么游戏一定会结束(但不一定是第个同学胜利,因为S的后缀与的前缀可能组成其他同学猜的串)。
如果第个串的后位与第个串前位相同,那么有的概率使第个同学胜利。
令,则珂以列出个方程。
又因为,所以这是一个元一次方程组。
所以用kmp求出所有,用高斯消元求解数组即珂。
毒瘤代码
#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;
}