HDU - 5955 AC自动机 + Gauss消元

题意:

N个人玩游戏,每个人心中猜测一个只允许有1到6数字,且长度为l的序列,题目保证任意两个人猜测的序列不完全相同。有一个色子,每次可以掷出1到6的任意数字,色子会永远不停的投掷,从而形成一个无限长的随机序列,问某一时刻,某个人所猜测的序列正好是这个序列末尾l个字符的概率是多少,输出每个人的概率。

思路:

lrj的《训练指南》上的ac自动机章节有过类似的题型,所以看到这种题,数据量也不大,首先考虑搞一个ac自动机来存储每个人的所猜测的序列。
本题的难点在于色子是无限投掷的,而lrj的题上的步数是确定的,所以那题的dp方式并不能直接运用到这个问题上。
体会ac自动机的原理,色子的每一次投掷一定都会在ac自动机上移动,在当前节点匹配,如果匹配上了当前字符就会前往下一个节点,如果匹配失败就会顺着失配边回到某个节点。注意到如果当前已经移动到了自动机上的匹配的末尾节点,说明从根到当前节点的字符串已经被匹配上。
对于每个人猜测的字符串,我们设从ac自动机上某个节点u能走到这个字符串的匹配末尾节点的概率Pu。
可以发现几点:

  1. 我们所要求的就是P0,也就是自动机初始节点的概率。
  2. 对于所有的末尾匹配节点,如果是当前字符的,概率就是1,否则概率就是0,因为在匹配到当前人所猜测字符之前不能先匹配到其他人的。
  3. 将失配边和匹配边一视同仁,那ac自动机就是一个有向图。对于每个节点,都有1,2,3,4,5,6这6条边指向其他节点,分别对应的概率都是1/6。

这样就可以对每个节点都列一个方程。ac自动机一共sz个节点就会有sz个方程,其中末尾节点的值已经确定。利用高斯消元解出x0就是所求答案。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXNODE = 1005;
const int MAXN = 1005;

int match[MAXN]; // 标记匹配节点的编号
map <string, int> ms;

struct Aho_Corasick {
    int ch[MAXNODE][10], sz;
    int val[MAXNODE], last[MAXNODE], f[MAXNODE];

    void init() {
        sz = 1; ms.clear();
        f[0] = last[0] = val[0] = 0;
        memset(ch[0], 0, sizeof(ch[0]));
    }

    int idx(char c) {return c - '1';}

    void Insert(char *s, int id) {
        int u = 0, l = strlen(s);
        for (int i = 0; i < l; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = ms[string(s)] = id;
        match[id] = u;          //记录匹配节点的编号
    }

    void getFail() {
        queue <int> que; f[0] = 0;
        for (int c = 0; c < 6; c++) {
            int u = ch[0][c];
            if (u) { f[u] = 0; que.push(u); last[u] = 0; }
        }
        while (!que.empty()) {
            int r = que.front(); que.pop();
            if (val[f[r]]) val[r] = 1;
            for (int c = 0; c < 6; c++) {
                int u = ch[r][c];
                if (!u) { ch[r][c] = ch[f[r]][c]; continue; }
                que.push(u);
                int v = f[r];
                while (v & !ch[v][c]) v = f[v];
                f[u] = ch[v][c];
                last[u] = val[f[u]] ? f[u] : last[f[u]];
            }
        }
    }
}ac;

int equ, var;
double a[MAXN][MAXN], x[MAXN];

int Gauss() {
    int i, j, k, col, max_r;
    for(k = 0, col = 0; k < equ && col < var; k++, col++){
        max_r = k;
        for(i = k + 1; i < equ; i++){
            if(fabs(a[i][col]) > fabs(a[max_r][col]))
                max_r = i;
        }
        if(k != max_r)
            for(j = col; j <= var; j++)
                swap(a[k][j], a[max_r][j]);
        for(i = k + 1; i < equ; i++){
            if(a[i][col]){
                double tmp = -a[i][col] / a[k][col];
                for(j = col; j <= var; j++)
                    a[i][j] += tmp * a[k][j];
            }
        }
    }
    for (i = equ - 1; i >= 0; i--) {
        double tmp = a[i][var];
        for (j = i + 1; j < var; j++)
            tmp -= x[j] * a[i][j] ;
        x[i] = tmp / a[i][i];
    }
    return 1;
}

char str[50];
bool vis[MAXN];

int main() {
    //freopen("in", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, l, tt;
        ac.init();
        scanf("%d%d", &n, &l);
        memset(vis, false, sizeof(vis));
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < l; j++) {
                scanf("%d", &tt);
                str[j] = tt + '0';
            }
            str[l] = '\0';
            ac.Insert(str, i);
            vis[match[i]] = true;    // 将匹配节点标记
        }
        ac.getFail();
        for (int i = 1; i <= n; i++) {
            equ = ac.sz; var = ac.sz;
            memset(a, 0, sizeof(a));
            memset(x, 0, sizeof(x));
            for (int j = 1; j <= n; j++)
                if (j == i) a[match[j]][var] = 1.0;
            for (int u = 0; u < ac.sz; u++) {
                a[u][u] = 1.0;
                if (vis[u]) continue;
                for (int c = 0; c < 6; c++) {
                    int v = ac.ch[u][c];
                    a[u][v] -= 1.0 / 6;
                }
            }
            Gauss();
            if (i != 1) printf(" ");
            printf("%.6f", x[0]);
        }
        printf("\n");
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值