题目大意:给定n个长度为l的模式串,现在要用前m个大写字母生成一个随机串,每个字符有自己的出现几率,第一次出现的字符串获胜,求最终每个字符串的获胜几率
建出AC自动机,搞出转移矩阵
如果某个节点是模式串那么这个节点只向自己连一条概率为1的出边
然后把转移矩阵自乘50遍即可
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 120 using namespace std; int n,l,m,limit; int pos[M]; char s[M]; double possibility[M]; namespace Aho_Corasick_Automaton{ struct Trie{ Trie *son[10],*fail; int ed; }*root,mempool[M],*C=mempool; void Insert(Trie *&p,char *s,int pos) { if(!p) p=++C; if(!*s) { p->ed=pos; ::pos[pos]=p-mempool; return; } Insert(p->son[*s-'A'],s+1,pos); } void Build_Tree() { static Trie *q[M]; int i,r=0,h=0; for(i=0;i<m;i++) if(root->son[i]) (q[++r]=root->son[i])->fail=root; else root->son[i]=root; while(r!=h) { Trie *p=q[++h]; for(i=0;i<m;i++) if(p->son[i]) (q[++r]=p->son[i])->fail=p->fail->son[i]; else p->son[i]=p->fail->son[i]; } } } class Matrix{ private: double xx[M][M]; public: Matrix() { memset(xx,0,sizeof xx); } Matrix(bool) { int i; memset(xx,0,sizeof xx); for(i=0;i<=limit;i++) xx[i][i]=1; } double* operator [] (int x) { return xx[x]; } friend void operator *= (Matrix &x,Matrix y) { int i,j,k; Matrix z; for(i=1;i<=limit;i++) for(j=1;j<=limit;j++) for(k=1;k<=limit;k++) z[i][j]+=x[i][k]*y[k][j]; x=z; } }f; int main() { using namespace Aho_Corasick_Automaton; int i,x,p,q; cin>>n>>l>>m; for(i=0;i<m;i++) { scanf("%d%d",&p,&q); possibility[i]=(double)p/q; } for(i=1;i<=n;i++) { scanf("%s",s+1); Insert(root,s+1,i); } Build_Tree(); limit=C-mempool; for(i=1;i<=limit;i++) { if(mempool[i].ed) f[i][i]=1; else for(x=0;x<m;x++) f[i][mempool[i].son[x]-mempool]+=possibility[x]; } for(i=1;i<=50;i++) f*=f; for(i=1;i<=n;i++) printf("%.2lf\n",f[1][pos[i]]); return 0; }
BZOJ 1444 JSOI2009 有趣的游戏 AC自动机+矩阵乘法
原文地址:http://blog.csdn.net/popoqqq/article/details/43485337