HYSBZOJ 1444 [Jsoi2009]有趣的遊戲
題目大意
分析
考慮我們已經得到了一個串,我們將這個串拿去和玩家的串匹配,最好的方法是用 AC 自動機:讓這個串在上面跑,找到第一個有 endpos 標記的地方。
那麼動態的想法就是在 AC 自動機上,設在第個節點上停止的概率,通過 fail 指針和 Trie 樹來轉移,我們可以得到:
其中表示點表示的字符生成的概率。是在 AC 自動機上能夠走到(通過 fail 指針和 Trie 樹上的邊)的點。
注意的是當一個點有 endpos 標記的時候,不能轉移到其他節點。
由於必須一開始就經過,所以我們強制定義。
參考代碼
#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;
}