poj 2778 AC自动机构建有向图 + 邻接矩阵快速幂

Problem:

给你m个病毒串,求指定长度n且不含病毒串作为子串的字符串一共有多少种.

Analyse:

用AC自动机构建L个状态节点,每个节点的end标记记录是否在这里形成病毒串.

这里有个核心就是,如果当前后缀的子后缀(也就是它的fail指针指向的地方)是病毒串的话,

那么它就是病毒串.

然后根据这个AC自动机的L个节点来建立有向图的邻接矩阵B,B[i][j]代表从i到j状态的路径数量.

B[0][j]代表的是从初始状态,还没有任何字符的时候转移到j状态,因为根节点0就是没有任何限制.

然后Bk之后,B[i][j]代表的是,从i到j长度为k的路径有多少条.

我们需要求得就是从根节点出发到任意节点,长度为n的路径的条数.

/**********************jibancanyang**************************
 *Author*        :jibancanyang
 *Created Time*  : 一  5/ 9 11:49:40 2016
 *File Name*     : poj2778.cpp

**Code**:
***********************[email protected]**********************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
vector<int> vi;
#define pr(x) cout << #x << ": " << x << "  "
#define pl(x) cout << #x << ": " << x << endl;
#define pri(a) printf("%d\n",(a));
#define xx first
#define yy second
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
#define sai(n) scanf("%I64d", &(n))
#define vep(c) for(decltype((c).begin() ) it = (c).begin(); it != (c).end(); it++)
const int mod = int(1e5) + 0, INF = 0x3fffffff;
const int maxn = 1e5 + 13;

const int maxtrie = 11 * 10 + 12; //注意这里为最多500个单词,每个单词最多500个字母,所以节点最多为乘
const int maxcharset = 4; //字符集合
const int charst = 0;
char buf[11];

class matrix
{
public:
    ll M[maxtrie][maxtrie], n;
    matrix (int N){
        n = N;
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
                M[i][j] = 0;
    }
    matrix(int N, int x) {
        n = N;
        for (int i = 0; i < N; i++) M[i][i] = x;
    }
    matrix operator* (const matrix &b) {
        matrix &a = *this;
        matrix ret(b.n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                ret.M[i][j] = 0;
                for (int k = 0; k < n; k++) {
                    ret.M[i][j] += a.M[i][k] * b.M[k][j]  % mod;
                }
                ret.M[i][j] = ret.M[i][j] % mod;
            }
        }
        return ret;
    }

    matrix operator^ (int b) {
        matrix ret(this -> n, 1);
        matrix a = *this;
        while (b) {
            if (b & 1) ret = ret * a;
            a = a * a;
            b >>= 1;
        }
        return ret;
    }

};

struct Trie
{
    int next[maxtrie][maxcharset], fail[maxtrie], end[maxtrie];
    int root, L;
    int newnode(void) {
        for (int i = 0;i < maxcharset;i++) next[L][i] = 0;
        end[L++] = 0;
        return L - 1;
    }
    int getid(char c) {
        if (c == ‘A‘) return 0;
        if (c == ‘T‘) return 1;
        if (c == ‘C‘) return 2;
        if (c == ‘G‘) return 3;
        return 0;
    }
    void init(void) {
        L = 0;
        root = newnode();
    }
    void insert(char buf[]) {
        int len = (int)strlen(buf);
        int now = root;
        for(int i = 0; i < len; i++) {
            if(next[now][getid(buf[i])] == 0)
                next[now][getid(buf[i])] = newnode();
            now = next[now][getid(buf[i])];
        }
        end[now] = true;
    }
    void build(void) {
        queue<int> Q;
        fail[root] = root;
        for(int i = 0;i < maxcharset;i++)
            if(next[root][i] == 0) next[root][i] = root;
            else {
                fail[next[root][i]] = root;
                Q.push(next[root][i]);
            }
        while (!Q.empty())  {
            int now = Q.front(); Q.pop();
            for(int i = 0;i < maxcharset;i++)
                if(next[now][i] == 0) {
                    next[now][i] = next[fail[now]][i];
                    end[now] = end[now] || end[fail[now]];
                }
                else {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
        }
    }
}ac;

int main(void)
{
#ifdef LOCAL
    freopen("/Users/zhaoyang/in.txt", "r", stdin);
    //freopen("/Users/zhaoyang/out.txt", "w", stdout);
#endif
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int m, k;
    while (~scanf("%d%d", &m, &k)) {
        ac.init();
        for (int i = 0; i < m; i++) {
            scanf("%s", buf);
            ac.insert(buf);
        }
        ac.build();
        matrix A(ac.L);
        for (int i = 0; i < ac.L; i++) {
            if (!ac.end[i]) {
                for (int j = 0; j < 4; j++) {
                    int temp = ac.next[i][j];
                    if (!ac.end[temp]) A.M[i][temp]++;
                }
            }
        }
        matrix B = A ^ k;
        /*for (int i = 0; i < ac.L; i++) {
            cout << i << ":  ";
            for (int j = 0; j < ac.L; j++) {
                cout << B.M[i][j] << " ";
            }
            cout << endl;
        }*/
        ll ans = 0;
        for (int i = 0; i < ac.L; i++) ans += B.M[0][i] % mod;
        printf("%lld\n", ans % mod);
    }

    return 0;
}
时间: 2024-12-27 17:08:03

poj 2778 AC自动机构建有向图 + 邻接矩阵快速幂的相关文章

poj 2778 AC自动机 + 矩阵快速幂

// poj 2778 AC自动机 + 矩阵快速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就可以得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自动机有一些特别,根节点是为空串,然而 // 每走一步的时候,如果没法走了,这时候,不一定是回到根 // 节点,因为有可能单个的字符时病毒,这样

poj 2778 AC自动机与矩阵连乘

http://poj.org/problem?id=2778 Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For example, if a animal's DNA sequence contains segment ATC then it may

POJ 2778 AC自动机+矩阵幂 不错的题

http://poj.org/problem?id=2778 有空再重新做下,对状态图的理解很重要 题解: http://blog.csdn.net/morgan_xww/article/details/7834801 另外做了矩阵幂的模板: //ac.sz是矩阵的大小 void mulmtr(long long x[MAXNODE][MAXNODE],long long y[MAXNODE][MAXNODE])//y=x*y { ll tmp[MAXNODE][MAXNODE]; for(in

poj 2778 AC自己主动机 + 矩阵高速幂

// poj 2778 AC自己主动机 + 矩阵高速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自己主动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就能够得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自己主动机有一些特别,根节点是为空串,然而 // 每走一步的时候,假设没法走了,这时候,不一定是回到根 // 节点,由于有可能单个的字符

poj2778 DNA Sequence【AC自动机】【矩阵快速幂】

DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 19991   Accepted: 7603 Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For ex

【CF696D】Legen...(AC自动机)(矩阵快速幂)

题目描述 Barney was hanging out with Nora for a while and now he thinks he may have feelings for her. Barney wants to send her a cheesy text message and wants to make her as happy as possible. Initially, happiness level of Nora is 0 0 . Nora loves some p

DNA Sequence POJ - 2778 AC 自动机 矩阵乘法

定义重载运算的时候一定要将矩阵初始化,因为这个调了一上午...... Code: #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<string> #define maxn 100000 typedef long long ll; using namespace std; void setIO(string a){ freopen((a+

POJ 4052 AC自动机

[题意]: 给你n个字符串和一个文本,问有多少个字符串满足如下条件:该字符串包含在文本,该字符串不为其它字符串的子串. [知识点]: Ac自动机,处理字符串 [题解]: 集训比赛的时候当时被题目的数据量吓到了,不敢用ac自动机.但在网上看到题解时,然后瞬间就感觉自己想多了... 很水的一道ac自动机处理字符串的题目,先将查询的字符做成字典树,然后在文本中查找字符串是否存在. 在处理去子串上,我的做法是查找文本时去除后缀相同的包含子串.然后单独在搜索剩余可用字符串前缀相同子串. [代码]: 1 #

POJ 3613 Cow Relays (floyd + 矩阵快速幂)

题目大意: 求刚好经过K条路的最短路 我们知道如果一个矩阵A[i][j] 表示表示 i-j 是否可达 那么 A*A=B  B[i][j]  就表示   i-j 刚好走过两条路的方法数 那么同理 我们把i-j 的路径长度存到A 中. 在A*A的过程中,不断取小的,那么最后得到的也就是i - j 走过两条路的最短路了. 当然也是利用到了floyd的思想. 然后要求出K次的最短路,那么就是矩阵快速幂的工作了. 注意要离散化.用map #include <cstdio> #include <io