【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
" , A.v[1][pos[i]]);
	return 0;
}

 

原文地址:https://www.cnblogs.com/GXZlegend/p/7115594.html