[BZOJ1444]有趣的游戏(AC自动机+矩阵乘法)
n个等长字符串,机器会随机输出一个字符串(每个字母出现的概率为p[i]),问每个字符串第一个出现的概率是多少。
显然建出AC自动机,套路地f[i][j]表示i时刻位于节点j的概率。
构建转移矩阵,当i为某个子串结束节点时A[i][i]=1,否则A[i][j]+=p[j]。
虽然事件总数无穷大,矩阵自乘50次就可以较为精确地得到结果了。
注意AC自动机的节点是从0开始的。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long double ld; 6 using namespace std; 7 8 const int N=110; 9 char str[N]; 10 ld x,y,p[N]; 11 int n,len,m,nd,q[N],fail[N],ch[N][12],pos[N],b[N]; 12 13 struct Mat{ 14 ld a[N][N]; 15 ld* operator [](int x){ return a[x]; } 16 Mat(){ memset(a,0,sizeof(a)); } 17 }A; 18 19 Mat mul(Mat &a,Mat &b){ 20 Mat c; 21 rep(i,0,nd) rep(j,0,nd) rep(k,0,nd) c[i][k]+=a[i][j]*b[j][k]; 22 return c; 23 } 24 25 void ins(char s[],int id){ 26 int x=0; 27 rep(i,1,len){ 28 int c=s[i]-'A'+1; 29 if (!ch[x][c]) ch[x][c]=++nd; 30 x=ch[x][c]; 31 } 32 pos[id]=x; b[x]=1; 33 } 34 35 void getfail(){ 36 int st=0,ed=0; 37 rep(i,1,m) if (ch[0][i]) q[++ed]=ch[0][i]; 38 while (st<ed){ 39 int x=q[++st]; 40 rep(i,1,m){ 41 if (ch[x][i]) fail[ch[x][i]]=ch[fail[x]][i],q[++ed]=ch[x][i]; 42 else ch[x][i]=ch[fail[x]][i]; 43 } 44 } 45 } 46 47 int main(){ 48 freopen("bzoj1444.in","r",stdin); 49 freopen("bzoj1444.out","w",stdout); 50 scanf("%d%d%d",&n,&len,&m); 51 rep(i,1,m) scanf("%Lf%Lf",&x,&y),p[i]=x/y; 52 rep(i,1,n) scanf("%s",str+1),ins(str,i); 53 getfail(); 54 rep(i,0,nd){ 55 if (b[i]) { A[i][i]=1; continue; } 56 rep(j,1,m) A[i][ch[i][j]]+=p[j]; 57 } 58 rep(i,1,50) A=mul(A,A); 59 rep(i,1,n) printf("%.2lf\n",(double)A[0][pos[i]]); 60 return 0; 61 }