[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 }

 

posted @ 2018-09-29 09:11  HocRiser  阅读(253)  评论(0编辑  收藏  举报