E - DNA Sequence POJ - 2778 (AC自动机+图论小知识+矩阵快速幂)

这道题目很有趣,我觉得很好玩。虽然不会做,看了题解才懂得快哭了

刚刚开始看这道题目,以为是dp,但是所求的字符长度n的大小有20亿, 就直接放弃了。然后看了题解, 看了半天才看懂。

首先构造AC自动机, 这步就是套模板。对每个输入的字符串尾部进行标记就可以了。 但是千万注意一点,当前节点是否被标记,还要考虑其失配指针指向的节点。

很好理解的。 比如当前输入两个串, abcd, bc。 如果直接套模板的话, 只会标记“abcd”中的d节点与bc中的c节点。 可是如果我们考虑一个字符串abc的匹配度的时候, 会得到它是合法的, 但是其实它的匹配度应该是不合法的, 因为bc被包含在abcd里面,所以如果我们同时考虑每个节点的失配节点的话,就会把abcd中的c节点也一同标记了(因为abcd中的c节点的失配节点是bc中的c, 由于bc中的c被标记了,所以abcd中的c也会被标记的)。这样就对了。(为了方便理解我举了以上数据)

好了下面构造矩阵了 map[n][n],n的大小由ac自动机中的总节点数加1得到(因为考虑了根节点)。map[i][j] 表示的是ac自动机中第i个个节点到直接到第j个节点的路径数。 在AC自动机里面每个节点会衍生出四条边(分别代表A T C G ),所以在AC自动机里面跑,就可以构造出map矩阵了。(在跑路径的时候,只要下一步到达的节点不是违法节点就可以累计上去)。

这样我们就可以构造出每个节点的到下一个节点的合法路径数。再对这个矩阵求n次方(用矩阵快速幂), 再对矩阵的第.0行累加就是我们要的答案了。

解释一下,这是个图论的知识, 如果一个图上有n个点,现在你有一个矩阵表示任意两点之间一步可以走到的路径数, 对于这个矩阵求 n次方, 那么得到的矩阵的第i, j个点,就是从第i点出发,走n步最后停在j点的方法数。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <math.h>
#include <queue>
using namespace std;
#define Max_N 200
struct Trie {
    int next[Max_N][5], end1[Max_N], fail[Max_N];
    int root, L;
    int newnode()
    {
        for (int i = 0; i < 5; i++)
            next[L][i] = -1;
        end1[L++] = 0;
        return L-1;
    }
    void init()
    {
        L = 0;
        root = newnode();
    }
    int ex_change(char c)
    {
        if (c == 'A') return 0;
        if (c == 'C') return 1;
        if (c == 'G') return 2;
        if (c == 'T') return 3;
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; i++) {
            int e = ex_change(buf[i]);
            if (next[now][e] == -1)
                next[now][e] = newnode();
            now = next[now][e];
        }
        end1[now] = 1;
    }

    void build()
    {
        queue<int> Q;
        fail[root] = root;
        for (int i = 0; i < 4; i++)
            if (next[root][i] == -1)
                next[root][i] = root;
            else
            {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while (!Q.empty()) {
            int now = Q.front();
            Q.pop();
            if(end1[fail[now]]) end1[now] = true;
            for (int i = 0; i < 4; i++) {
               if (next[now][i] == -1)
                    next[now][i] = next[fail[now]][i];
                else {
                    fail[next[now][i]] = next[fail[now]][i];
                    Q.push(next[now][i]);
                }
                //end1[now] = end1[now] || end1[fail[now]];
            }
        }
        /*for (int i = 0; i < L; i++)
            end1[i] = end1[i] || end1[fail[i]];*/
    }
};
#define N 150
#define Mod 100000
#define ll long long int
int n, m;
int _k;
char buf[100];
long long int map1[150][150];
Trie ac;
void Multi(ll a[][N], ll b[][N], ll c[][N]) {
    for (int i=0; i<n; i++)
        for (int j=0; j<n; j++) {
            c[i][j] = 0;
            for (int k=0; k<n; k++)
                c[i][j] = (c[i][j] + a[i][k]*b[k][j]) % Mod;
        }
}
//d = s
void copy(ll d[][N], ll s[][N]) {
    for (int i=0; i<n; i++) for (int j=0; j<n; j++)
        d[i][j] = s[i][j];
}
//a = a^b % Mod
void PowerMod(ll a[][N], ll b) {
    ll t[N][N], ret[N][N];
    for (int i=0; i<n; i++) ret[i][i] = 1;
    while (b) {
        if (b & 1) { Multi(ret, a, t); copy(ret, t); }
        Multi(a, a, t); copy(a, t);
        b >>= 1;
    }
    copy(a, ret);
}



int main()
{
    while(scanf("%d%d", &_k, &m) != EOF) {
        ac.init();
        for (int i = 1; i <= _k; i++) {
            scanf("%s", buf);
            ac.insert(buf);
        }
        ac.build();
        memset(map1, 0, sizeof(map1));
        for (int i = 0; i < ac.L; i++) {
                for (int j = 0; j < 4; j++) {
                    int u = ac.next[i][j];
                    if (!ac.end1[u]) map1[i][u]++;
                }

        }
       /* for (int i = 0; i < ac.L + 1; i++)
            map1[i][ac.L] = 1;*/
        n = ac.L;
        PowerMod(map1, m);
        long long int sum = 0;
        for (int i = 0; i < N; i++) {
            //cout <<endl;

               // printf("%d ", map1[i][j]);
                sum += map1[0][i];
                sum = sum % Mod;

        }

        cout << sum <<endl;
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值