硬币游戏

/*
    KMP+高斯消元 
    设N为未结束状态的概率。 
    假设用两个串TTH和HTT,设第一个获胜的概率是A,第二个人获胜的概率为B。 
    如果在N后面加上TTH,那么有三种可能。 
    NTTH=A+BTH+BH ,是什么意思呢?就是如果在N后面加入TTH,那么第一个人猜的序列出现在了硬币序列中,第一个人获胜,但是N是什么我们不清楚,但是有可能到达第一个T或者第二个T的时候第二个人就获胜了。 
    所以对于状态NTTH,可以由三个状态得到。 
    0.125N=A+0.25B+0.5B,0.5是正反面的概率。
    上述式子可以用kmp球的,然后高斯消元求解。
*/
#include<iostream>
#include<cstdio>
#define N 1010
using namespace std;
char s[N][N];
double ec[N],a[N][N];
int va[N],vb[N],n,m;
void init(){
    ec[m]=1.0;
    for(int i=m-1;i;i--) ec[i]=ec[i+1]*0.5;
    for(int i=1;i<=n;i++){
        va[1]=0;
        for(int k=2,p=0;k<=m;k++){
            while(p&&s[i][k]!=s[i][p+1]) p=va[p];
            if(s[i][k]==s[i][p+1]) p++;
            va[k]=p;
        }
        for(int j=1;j<=n;j++){
            vb[0]=0;
            for(int k=1,p=0;k<=m;k++){
                while(p&&s[j][k]!=s[i][p+1]) p=va[p];
                if(s[j][k]==s[i][p+1]) p++;
                vb[k]=p;
            }
            for(int now=vb[m];now;now=va[now]) a[i][j]+=ec[now];
        }
        a[i][n+1]=1;
    }
}
void gauss(){
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n+1;j++) a[i][j]/=a[i][i];
        a[i][i]=1;
        for(int j=i+1;j<=n;j++)
            for(int k=i+1;k<=n+1;k++)
                a[j][k]-=a[i][k]*a[j][i];
    }
    for(int i=n;i;i--)
        for(int j=i+1;j<=n;j++)
            a[i][n+1]-=a[i][j]*a[j][n+1];
    double sum=0,ans=0;
    for(int i=1;i<=n;i++) sum+=a[i][n+1];
    for(int i=1;i<=n;i++){
        ans=a[i][n+1];ans/=sum;
        printf("%.10f
",(double)ans);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    init();gauss();
    return 0;
}
原文地址:https://www.cnblogs.com/harden/p/6706139.html