bzoj4820
来源:互联网 发布:三星专用下载软件 编辑:程序博客网 时间:2024/06/03 16:45
题意:
给出n个互不相同长度为m的01串。有一个序列,初始为空。现在不停在序列尾部等概率添加一个0或1,直到序列后缀匹配n个串中的一个。对于每个01串
n,m<=300
#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#define N 310#define eps 1e-9using namespace std;long double a[N][N],nt[N],r[N];int n,m,ex[N];struct node{ char s[N]; int nex[N]; void pre() { nex[1]=m;nex[2]=m-1; for(int i=1;i<m;i++) if(s[i]!=s[i+1]) {nex[2]=i-1;break;} int k=2; for(int i=3;i<=m;i++) { int p=k+nex[k]-1,l=nex[i-k+1]; if(i+l-1<p) nex[i]=l; else { int j=p-i+1; if(j<0) j=0; while(i+j<=m && s[i+j]==s[1+j]) j++; nex[i]=j; k=i; } } }}A[N];void get_ex(char *s1,int *nex,char *s2){ for(int i=1;i<=m;i++) ex[i]=0; ex[1]=m; for(int i=1;i<=m;i++) if(s1[i]!=s2[i]) {ex[1]=i-1;break;} int k=1; for(int i=2;i<=m;i++) { int p=k+ex[k]-1,l=nex[i-k+1]; if(i+l-1<p) ex[i]=l; else { int j=p-i+1; if(j<0) j=0; while(i+j<=m && s1[1+j]==s2[i+j]) j++; ex[i]=j; k=i; } }}void gauss(){ for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) if(abs(a[j][i])>eps) {for(int k=0;k<=n;k++) swap(a[i][k],a[j][k]);break;} for(int j=i+1;j<=n;j++) //if(abs(a[j][i])>eps) { long double t=a[j][i]/a[i][i]; for(int k=0;k<=n;k++) a[j][k]-=a[i][k]*t; } } for(int i=n;i>=1;i--) { r[i]=a[i][0]/a[i][i]; for(int j=1;j<i;j++) a[j][0]-=a[j][i]*r[i]; }}int main(){ scanf("%d%d",&n,&m); nt[0]=1; for(int i=1;i<=m;i++) nt[i]=nt[i-1]*0.5; for(int i=1;i<=n;i++) {scanf("%s",A[i].s+1);A[i].pre();} for(int i=1;i<=n;i++) { a[i][i]--; for(int j=1;j<=n;j++) { if(i==j) for(int k=1;k<=m;k++) ex[k]=A[i].nex[k]; else get_ex(A[i].s,A[i].nex,A[j].s); for(int k=1;k<m;k++) if(ex[m-k+1]==k) a[i][j]-=nt[m-k]; } } n++; for(int i=1;i<n;i++) a[i][n]=1; for(int i=1;i<n;i++) a[n][i]=1; a[n][0]=1; gauss(); for(int i=1;i<n;i++) printf("%.10lf\n",(double)r[i]); return 0;}
题解:
感觉非常有意思的一道题><
暴力建个ac自动机然后高斯消元是
考虑匹配后也不停止,让序列继续增长,然后关注第一次匹配位置。
设
设
在所有的序列中,存在一个能在第j个位置匹配si的概率是
考虑减去在前面第一次出现的情况来求
那么对于一个在小于等于j-m的位置匹配的序列,它能在位置j匹配
对于一个在位置k第一次和
现在将
然后,我们关注的是每个多项式的系数之和,所以可以直接列出n条方程。
但是注意这部分的值我们是不知道的