来自FallDream的博客,未经允许,请勿转载,谢谢。
周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的n个序列两两不同。很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。
n,m<=300
只会(nm)^3的做法....正解太神啦。
令N表示什么都没匹配到的状态,然后我计算在后面接上一个串的概率
注意到N是什么其实是不确定的,也就是可能还没全部接上去就接好了
更详细地,假设A=HTT,B=TTH
那么p(N+A)=p(A)+p(B)*2^(-1)+p(B)*2^(-2)
p(N+A)=p(N)*2^(-3)
也就是说,有一个串的后缀是我的前缀的时候,它会影响我的概率。
求这种情况可以用kmp
这样就列出了n个方程,在加上一个概率和等于1的方程,就能得到n+1个变量n+1个方程 高斯消元即可。
复杂度n^3
#include<iostream> #include<cstdio> #define MN 300 #define ld long double using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,m,fail[MN+5][MN+5]; char st[MN+5][MN+5]; ld a[MN+5][MN+5],p[MN+5],ans[MN+5]; void BuildFail(char*s,int*f) { for(int i=2,j=0;i<=m;++i) { while(j&&s[j+1]!=s[i]) j=f[j]; if(s[j+1]==s[i])++j;f[i]=j; } } ld Calc(int y,int x) { ld ans=0;int j=0; for(int i=1;i<=m;++i) { while(j&&st[y][j+1]!=st[x][i]) j=fail[y][j]; if(st[y][j+1]==st[x][i]) ++j; } for(;j;) ans+=p[m-j],j=fail[y][j]; return ans; } void Gauss() { for(int i=1;i<=n+1;++i) { for(int j=i;j<=n+1;++j) if(a[j][i]) { if(j!=i) for(int k=i;k<=n+2;++k) swap(a[j][k],a[i][k]); break; } for(int j=i+1;j<=n+1;++j) { ld delta=a[j][i]/a[i][i]; for(int k=i;k<=n+2;++k) a[j][k]=a[j][k]-a[i][k]*delta; } } for(int i=n+1;i;--i) { for(int j=i+1;j<=n+1;++j) a[i][n+2]-=a[i][j]*ans[j]; ans[i]=a[i][n+2]/a[i][i]; } } int main() { n=read();m=read();p[0]=1; for(int i=1;i<=m;++i) p[i]=p[i-1]/2; for(int i=1;i<=n;++i) scanf("%s",st[i]+1),a[i][n+1]=-p[m]; for(int i=1;i<=n;++i) a[n+1][i]=1;a[n+1][n+2]=1; for(int i=1;i<=n;++i) BuildFail(st[i],fail[i]); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=Calc(i,j); Gauss(); for(int i=1;i<=n;++i) printf("%.10lf ",(double)ans[i]); return 0; }