週末同學們非常無聊,有人提議,咱們扔硬幣玩吧,誰扔的硬幣正面次數多誰勝利。
大家紛紛覺得這個遊戲非常符合同學們的特色,但只是扔硬幣實在是太單調了。
同學們覺得要加強趣味性,所以要找一個同學扔很多很多次硬幣,其他同學記錄下正反面情況。
用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;
}