1. 程式人生 > >[AC自動機+高斯消元] 求多個序列各自出現概率 HDU5955

[AC自動機+高斯消元] 求多個序列各自出現概率 HDU5955

Guessing the Dice Roll

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1566    Accepted Submission(s): 457  

Problem Description

There are N players playing a guessing game. Each player guesses a sequence consists of {1,2,3,4,5,6} with length L, then a dice will be rolled again and again and the roll out sequence will be recorded. The player whose guessing sequence first matches the last L rolls of the dice wins the game. 

Input

The first line is the number of test cases. For each test case, the first line contains 2 integers N (1 ≤ N ≤ 10) and L (1 ≤ L ≤ 10). Each of the following N lines contains a guessing sequence with length L. It is guaranteed that the guessing sequences are consist of {1,2,3,4,5,6} and all the guessing sequences are distinct.

Output

For each test case, output a line containing the winning probability of each player with the precision of 6 digits.

Sample Input

3

5 1

1

2

3

4

5

6 2

1 1

2 1

3 1

4 1

5 1

6 1

4 3

1 2 3

2 3 4

3 4 5

4 5 6

Sample Output

0.200000 0.200000 0.200000 0.200000 0.200000

0.027778 0.194444 0.194444 0.194444 0.194444 0.194444

0.285337 0.237781 0.237781 0.239102

Source

#include <bits/stdc++.h>
#define eps 1e-9
using namespace std;

const int mn = 13 * 13;

int n, l;

int nx[mn][10], fail[mn];
bool ed[mn];
int mk[mn];
int root, L;
int newnode()
{
	for (int i = 1; i <= 6; i++)
		nx[L][i] = -1;
	ed[L++] = 0;
	return L - 1;
}
void init()
{
	L = 0;
	root = newnode();
}
void add(int g[], int id)
{
	int now = root;
	for (int i = 0; i < l; i++)
	{
		if (nx[now][g[i]] == -1)
			nx[now][g[i]] = newnode();
		now = nx[now][g[i]];
	}
	ed[now] = 1;
	mk[id] = now;
}
void build_Fail()
{
	queue<int> Q;
	fail[root] = root;
	for (int i = 1; i <= 6; i++)
	{
		if (nx[root][i] == -1)
			nx[root][i] = root;
		else 
		{
			fail[nx[root][i]] = root;
			Q.push(nx[root][i]);
		}
	}
	while (!Q.empty())
	{
		int now = Q.front();
		Q.pop();
		for (int i = 1; i <= 6; i++)
		{
			if (nx[now][i] == -1)
				nx[now][i] = nx[fail[now]][i];
			else 
			{
				fail[nx[now][i]] = nx[fail[now]][i];
				Q.push(nx[now][i]);
			}
		}
	}
}

double a[mn][mn], x[mn];
void Gauss(int equ, int var)
{
	int col = 0, max_r;
	for (int k = 0; k < equ && col < var; k++, col++)
	{
		max_r = k;
		for (int i = k + 1; i < equ; i++)
			if (fabs(a[i][col]) > fabs(a[max_r][col]))
				max_r = i;
		if (fabs(a[max_r][col]) < eps)
			return;
		if (k != max_r)
		{
			for (int j = col; j < var; j++)
				swap(a[k][j], a[max_r][j]);
			swap(x[k], x[max_r]);
		}
		x[k] /= a[k][col];
		for (int j = col + 1; j < var; j++)
			a[k][j] /= a[k][col];
		a[k][col] = 1;
		for (int i = 0; i < equ; i++)
			if (i != k)
		{
			x[i] -= x[k] * a[i][col];
			for (int j = col + 1; j < var; j++)
				a[i][j] -= a[k][j] * a[i][col];
			a[i][col] = 0;
		}
	}
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		init();
		scanf("%d %d", &n, &l);
		for (int i = 0; i < n; i++)
		{
			int g[mn];
			for (int j = 0; j < l; j++)
				scanf("%d", &g[j]);
			add(g, i);
		}
		build_Fail();
		// 構造可勝序列的Fail指標表示狀態轉移情況
		
		memset(a, 0, sizeof a);
		memset(x, 0, sizeof x);
		
		/// 初始化 移項, 變號
		for (int i = 0; i < L; i++)
			a[i][i] = -1.0;	
		x[0] = -1.0;
		
		for (int i = 0; i < L; i++)
		{
			for (int j = 1; j <= 6; j++)
			{
				if (!ed[i] && nx[i][j] != -1)
				a[nx[i][j]][i] += 1.0 / 6;
				/// i 可到 t , 第 t 行第 i 項 + 1 / 6
			}
		}
		
		Gauss(L, L); /// 高斯消元解方程
		for (int i = 0; i < n - 1; i++)
			printf("%.6lf ", x[mk[i]]);
		printf("%.6lf\n", x[mk[n - 1]]);
	}
	return 0;
}