bzoj 1444 AC自动机 + 矩阵乘法 | 高斯消元

恶补了一下AC自动机,花了一天时间终于全部搞明白了。

思路:将每个人的串加入AC自动机,在AC自动机生成的状态图上建边,注意单词末尾的节点只能转移到自己概率为1,

然后将矩阵自乘几十次后误差就很小了, 或者可以高斯消元搞出精确解。

#include<bits/stdc++.h>
#define LL long long
#define ll long long
#define fi first
#define se second
#define mk make_pair
#define pii pair<int, int>
#define y1 skldjfskldjg
#define y2 skldfjsklejg

using namespace std;

const int N = 100 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;

int n, l, m, pos[11];
double pro[11];
char s[11];

struct Matrix {
    int r, c;
    double a[101][101];
    Matrix(int r = 0, int c = 0) {
        this->r = r;
        this->c = c;
        memset(a, 0, sizeof(a));
    }

    Matrix operator * (const Matrix &B) const {
        Matrix C(r, c);
        for(int i = 0; i < r; i++)
            for(int j = 0; j < c; j++)
                for(int k = 0; k < r; k++)
                    C.a[i][j] += a[i][k] * B.a[k][j];
        return C;
    }
};

struct Ac {
    int val[N], ch[N][26], f[N], last[N], cnt, SZ;

    void init(int SZ = 26) {
        cnt = 0; this->SZ = SZ;
        for(int c = 0; c < SZ; c++) ch[0][c] = 0;
    }

    int getId(char c) {
        return c - 'A';
    }

    int newNode() {
        cnt++;
        memset(ch[cnt], 0, sizeof(ch[cnt]));
        val[cnt] = f[cnt] = last[cnt] = 0;
        return cnt;
    }

    void add(char *s, int &pos) {
        int u = 0;
        for(int i = 0; s[i]; i++) {
            int c = getId(s[i]);
            if(!ch[u][c]) ch[u][c] = newNode();
            u = ch[u][c];
        }
        val[u]++;
        pos = u;
    }

    void build() {
        queue<int> que;
        f[0] = 0;
        for(int c = 0; c < SZ; c++) {
            if(!ch[0][c]) continue;
            f[ch[0][c]] = last[ch[0][c]] = 0;
            que.push(ch[0][c]);
        }
        while(!que.empty()) {
            int u = que.front(); que.pop();
            for(int c = 0; c < SZ; c++) {
                int v = ch[u][c];
                if(!v) {
                    ch[u][c] = ch[f[u]][c];
                    continue;
                } else {
                    que.push(v);
                    f[v] = ch[f[u]][c];
                    last[v] = val[f[v]] ? f[v] : last[f[v]];
                }
            }
        }
    }

    void buildMatrix(Matrix &A) {
        for(int u = 0; u <= cnt; u++) {
            if(val[u]) A.a[u][u] = 1;
            else {
                for(int c = 0; c < m; c++) {
                    int v = ch[u][c];
                    A.a[u][v] += pro[c];
                }
            }
        }
    }
} ac;

int main() {
    scanf("%d%d%d", &n, &l, &m);
    for(int i = 0; i < m; i++) {
        double p, q;
        scanf("%lf%lf", &p, &q);
        pro[i] = p / q;
    }

    ac.init(m);

    for(int i = 1; i <= n; i++) {
        scanf("%s", s);
        ac.add(s, pos[i]);
    }

    ac.build();
    Matrix A(ac.cnt + 1, ac.cnt + 1);
    ac.buildMatrix(A);

    for(int i = 1; i <= 40; i++)
        A = A * A;

    for(int i = 1; i <= n; i++) printf("%.2f\n", A.a[0][pos[i]]);
    return 0;
}


/*
*/

 

转载于:https://www.cnblogs.com/CJLHY/p/9465075.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值