【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
题目描述
输入
注意 是0<=P
输出
样例输入
样例输出
题解
AC自动机+矩阵乘法
先将所有字符串放到AC自动机中,求出Trie图。
然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向next[x][i],边权为pi/qi;否则x只连向x,边权为1。
然后这个矩阵的无穷次方即为答案。
由于这个矩阵乘了很多次后概率基本不变,可以认定为答案。所以我们可以将这个矩阵自乘50次(相当于求出这个矩阵的2^50次方),得出答案。
#include <cstdio> #include <cstring> #include <queue> #define N 12 #define M 110 using namespace std; queue<int> q; double p[N]; int pos[N] , next[M][N] , fail[M] , tag[M], tot = 1 , m; char str[N]; struct data { double v[M][M]; data() {memset(v , 0 , sizeof(v));} data operator*(const data a)const { data ans; int i , j , k; for(i = 1 ; i <= tot ; i ++ ) for(j = 1 ; j <= tot ; j ++ ) for(k = 1 ; k <= tot ; k ++ ) ans.v[i][j] += v[i][k] * a.v[k][j]; return ans; } }A; void build() { int x , i; for(i = 0 ; i < m ; i ++ ) next[0][i] = 1; q.push(1); while(!q.empty()) { x = q.front() , q.pop(); for(i = 0 ; i < m ; i ++ ) { if(next[x][i]) fail[next[x][i]] = next[fail[x]][i] , q.push(next[x][i]); else next[x][i] = next[fail[x]][i]; } } } int main() { int n , l , i , j , x , y; scanf("%d%d%d" , &n , &l , &m); for(i = 0 ; i < m ; i ++ ) scanf("%d%d" , &x , &y) , p[i] = (double)x / y; for(i = 1 ; i <= n ; i ++ ) { scanf("%s" , str + 1); for(x = j = 1 ; j <= l ; j ++ ) { if(!next[x][str[j] - 'A']) next[x][str[j] - 'A'] = ++tot; x = next[x][str[j] - 'A']; } pos[i] = x , tag[x] = 1; } build(); for(i = 1 ; i <= tot ; i ++ ) { if(tag[i]) A.v[i][i] = 1; else for(j = 0 ; j < m ; j ++ ) A.v[i][next[i][j]] += p[j]; } for(i = 1 ; i <= 50 ; i ++ ) A = A * A; for(i = 1 ; i <= n ; i ++ ) printf("%.2lf\n" , A.v[1][pos[i]]); return 0; }