bzoj 4820: [Sdoi2017]硬币游戏【kmp+高斯消元】
有点神,按照1444的做法肯定会挂
注意到它的概率是相同的,所以可以简化状态
详见http://www.cnblogs.com/candy99/p/6701221.html
https://www.cnblogs.com/liu-runda/p/6919077.html
总之就是靠在kmp中的ne数组上跳来找到对当前串概率有影响的串然后把概率加起来……
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=305;
int n,m,ne[N][N];
double a[N][N],p[N];
char c[N][N];
void gtne(char s[],int ne[])
{
for(int i=2,j=0;i<=m;i++)
{
while(j&&s[j+1]!=s[i])
j=ne[j];
if(s[j+1]==s[i])
j++;
ne[i]=j;
}
}
void gaosi(int n)
{
for(int i=1;i<=n;i++)
{
int nw=i;
for(int j=i+1;j<=n;j++)
if(fabs(a[j][i])>fabs(a[nw][i]))
nw=j;
for(int j=i;j<=n+1;j++)
swap(a[i][j],a[nw][j]);
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[j][i]*a[i][k];
a[j][i]=0;
}
}
for(int i=n-1;i>=1;i--)
{
for(int j=i+1;j<=n;j++)
a[i][n+1]-=a[j][n+1]*a[i][j];
a[i][n+1]/=a[i][i];
}
}
int main()
{
scanf("%d%d",&n,&m);
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",c[i]+1),a[i][n+1]=-p[m],a[n+1][i]=1;
a[n+1][n+2]=1;
for(int i=1;i<=n;i++)
gtne(c[i],ne[i]);
for(int y=1;y<=n;y++)
for(int x=1;x<=n;x++)
{
int j=0;
for(int i=1;i<=m;++i)
{
while(j&&c[y][j+1]!=c[x][i])
j=ne[y][j];
if(c[y][j+1]==c[x][i])
j++;
}
while(j)
a[y][x]+=p[m-j],j=ne[y][j];
}
gaosi(n+1);
for(int i=1;i<=n;i++)
printf("%.10f\n",a[i][n+2]);
return 0;
}