【HYSBZOJ】【AC自動機】【期望DP】1444 [Jsoi2009]有趣的遊戲

HYSBZOJ 1444 [Jsoi2009]有趣的遊戲

題目大意

◇題目傳送門◆

分析

考慮我們已經得到了一個串,我們將這個串拿去和玩家的串匹配,最好的方法是用 AC 自動機:讓這個串在上面跑,找到第一個有 endpos 標記的地方。

那麼動態的想法就是在 AC 自動機上,設f(i)f(i)在第ii個節點上停止的概率,通過 fail 指針和 Trie 樹來轉移,我們可以得到:

f(u)=f(v)×Pc f(u)=\sum f(v)\times P_c

其中PcP_c表示uu點表示的字符cc生成的概率。vvuu在 AC 自動機上能夠走到(通過 fail 指針和 Trie 樹上的邊)的點。

注意的是當一個點有 endpos 標記的時候,不能轉移到其他節點。

由於f(root)f(root)必須一開始就經過,所以我們強制定義f(root)=f(v)+1f(root)=\sum f(v) + 1

參考代碼

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int Maxn = 10;
const double EPS = 1e-6;

int N, M, L;
double P[Maxn + 5];

struct ACAutomaton {
	struct TrieNode {
		int endpos;
		TrieNode *fail;
		TrieNode *ch[Maxn];
	};
	TrieNode pool[Maxn * Maxn * 4 + 5];
	TrieNode *root, *NIL, *ncnt;
	int cnt;
	ACAutomaton () {
		NIL = ncnt = &pool[0];
		NIL->endpos = -1, NIL->fail = NIL;
		for(int i = 0; i < Maxn; i++)
			NIL->ch[i] = NIL;
		root = newnode(), cnt = 1;
	}
	TrieNode *newnode() {
		TrieNode *p = ++ncnt;
		p->endpos = -1, p->fail = NIL;
		for(int i = 0; i < Maxn; i++)
			p->ch[i] = NIL;
		cnt++;
		return p;
	}
	void insert(char *s, int k) {
		int lens = strlen(s);
		TrieNode *p = root;
		for(int i = 0; i < lens; i++) {
			int c = (int)s[i] - 'A';
			if(p->ch[c] == NIL) p->ch[c] = newnode();
			p = p->ch[c];
		}
		p->endpos = k;
	}
	void build() {
		queue<TrieNode *> q;
		for(int i = 0; i < Maxn; i++)
			if(root->ch[i] != NIL) {
				root->ch[i]->fail = root;
				q.push(root->ch[i]);
			}
		while(!q.empty()) {
			TrieNode *p = q.front();
			q.pop();
			for(int i = 0; i < Maxn; i++)
				if(p->ch[i] != NIL) {
					TrieNode *pre;
					for(pre = p->fail; pre != NIL; pre = pre->fail)
						if(pre->ch[i] != NIL) {
							p->ch[i]->fail = pre->ch[i];
							break;
						}
					if(pre == NIL) p->ch[i]->fail = root;
					q.push(p->ch[i]);
				}
		}
	}
	inline int getpos(TrieNode *p) {
		return p - pool;
	}
	TrieNode *findson(TrieNode *p, int c) {
		for(; p != NIL; p = p->fail)
			if(p->ch[c] != NIL) break;
		return p == NIL ? root : p->ch[c];
	}
	void prepare(double g[][Maxn * Maxn + 5]) {
		for(int i = 1; i <= cnt; i++) {
			g[i][i] = -1;
			if(pool[i].endpos >= 0) continue;
			for(int c = 0; c < M; c++) {
				TrieNode *tmp = findson(&pool[i], c);
				g[getpos(tmp)][i] += P[c + 1];
			}
		}
		g[1][cnt + 1] = -1;
	}
};
ACAutomaton f;

double g[Maxn * Maxn + 5][Maxn * Maxn + 5];
void Gauss(int n) {
	for(int i = 1; i <= n; i++) {
		int to = i;
		for(; to <= n; to++)
			if(fabs(g[to][i]) > EPS) break;
		if(to > n) break;
		if(to != i)
			for(int j = 1; j <= n + 1; j++)
				swap(g[to][j], g[i][j]);
		double t = g[i][i];
		for(int j = 1; j <= n + 1; j++)
			g[i][j] /= t;
		for(int j = 1; j <= n; j++)
			if(j != i) {
				t = g[j][i];
				for(int k = 1; k <= n + 1; k++)
					g[j][k] -= t * g[i][k];
			}
	}
}

double ans[Maxn + 5];
void getans() {
	for(int i = 1; i <= f.cnt; i++)
		if(f.pool[i].endpos >= 0)
			ans[f.pool[i].endpos] = g[i][f.cnt + 1];
}

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d %d %d", &N, &L, &M);
	for(int i = 1; i <= M; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		P[i] = 1.0 * x / y;
	}
	for(int i = 1; i <= N; i++) {
		char s[Maxn + 5];
		scanf("%s", s);
		f.insert(s, i);
	}
	f.build();
	f.prepare(g);
	Gauss(f.cnt);
	getans();
	for(int i = 1; i <= N; i++)
		printf("%.2f\n", ans[i] + EPS);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章