POJ2778&HDU2243&POJ1625(AC自动机+矩阵/DP)

POJ2778

题意:只有四种字符的字符串(A, C, T and G),有M中字符串不能出现,为长度为n的字符串可以有多少种。

题解:在字符串上有L中状态,所以就有L*A(字符个数)中状态转移。这里自动机的build的hdu2222略有不同。

那一题中通过询问时循环来求she的he,但是如果he不能出现,she就算不是禁止的字符串也不可出现,所以在build的时候就记录所有不能出现的状态。

if (end[ fail[now] ]) end[now]++;

然后用一个矩阵F来记录可以相互到达的状态就OK了。

矩阵可以理解为用长度为1的字符串两个状态可以相互达到,而F=F^n,F[i][j]就是字符串长度为n时状态i到状态j的路径。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <set>
using namespace std;

const int N = 150;
const int A = 4;
const int M = 15;
const int MOD = 100000;

typedef vector<int> vec;
typedef vector<vec> mat;

mat mul(mat &A, mat &B)
{
    mat C(A.size(), vec(B[0].size()));
    for (int i = 0; i < A.size(); ++i) {
        for (int k = 0; k < B.size(); ++k) {
            for (int j = 0; j < B[0].size(); ++j) {
                C[i][j] = (C[i][j] + (long long)A[i][k] * B[k][j]) % MOD;
            }
        }
    }
    return C;
}

// A^n
mat pow(mat A, int n)
{
    mat B(A.size(), vec(A.size()));
    for (int i = 0; i < A.size(); ++i)
        B[i][i] = 1;
    while (n > 0) {
        if (n & 1) B = mul(B, A);
        A = mul(A, A);
        n >>= 1;
    }
    return B;
}

struct ACAutomata {

    int next[N][A], fail[N], end[N];
    int root, L;

    int idx(char ch)
    {
        switch(ch) {
            case ‘A‘: return 0;
            case ‘C‘: return 1;
            case ‘T‘: return 2;
            case ‘G‘: return 3;
        }
        return -1;
    }
    int newNode()
    {
        for (int i = 0; i < A; ++i) next[L][i] = -1;
        end[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newNode();
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; ++i) {
            int ch = idx(buf[i]);
            if (next[now][ch] == -1) next[now][ch] = newNode();
            now = next[now][ch];
        }
        end[now]++;
    }
    void build()
    {
        queue<int> Q;
        fail[root] = root;
        for (int i = 0; i < A; ++i) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            } else {
                fail[ next[root][i] ] = root;
                Q.push( next[root][i] );
            }
        }
        while (Q.size()) {
            int now = Q.front();
            Q.pop();
            if (end[ fail[now] ]) end[now]++; //!!
            for (int i = 0; i < A; ++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]);
                }
            }
        }
    }

    int query(int n)
    {
        mat F(L, vec(L));
        for (int i = 0; i < L; ++i) {
            for (int j = 0; j < L; ++j) {
                F[i][j] = 0;
            }
        }
        for (int i = 0; i < L; ++i) {
            for (int j = 0; j < A; ++j) {
                int nt = next[i][j];
                if (!end[nt]) F[i][nt]++;
            }
        }
        F = pow(F, n);
        int res = 0;
        for (int i = 0; i < L; ++i) {
            res = (res + F[0][i]) % MOD;
        }
        return res;
    }

} ac;

char buf[20];
int main()
{
    int m, n;
    while (~scanf("%d%d", &m, &n)) {
        ac.init();
        while (m--) {
            scanf("%s", buf);
            ac.insert(buf);
        }
        ac.build();
        printf("%d\n", ac.query(n));
    }

    return 0;
}

HDU 2243上题要求不存在给定字符串,而这题要求存在至少一个。于是方法就是总方法减去不存在的就是答案了。

题目要求是小于等于L的,所以矩阵加一维记录前缀和就好了。

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;
typedef vector<ull> vec;
typedef vector<vec> mat;
const int N = 150;
const int A = 26;
const int M = 15;

mat mul(mat &A, mat &B)
{
    mat C(A.size(), vec(B[0].size()));
    for (int i = 0; i < A.size(); ++i) {
        for (int k = 0; k < B.size(); ++k) {
            for (int j = 0; j < B[0].size(); ++j) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

mat pow(mat A, ull n)
{
    mat B(A.size(), vec(A.size()));
    for (int i = 0; i < A.size(); ++i)
        B[i][i] = 1;
    while (n > 0) {
        if (n & 1) B = mul(B, A);
        A = mul(A, A);
        n >>= 1;
    }
    return B;
}

struct ACAutomata {

    int next[N][A], fail[N], end[N];
    int root, L;

    int idx(char ch)
    {
        return ch - ‘a‘;
    }
    int newNode()
    {
        for (int i = 0; i < A; ++i) next[L][i] = -1;
        end[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newNode();
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; ++i) {
            int ch = idx(buf[i]);
            if (next[now][ch] == -1) next[now][ch] = newNode();
            now = next[now][ch];
        }
        end[now]++;
    }
    void build()
    {
        queue<int> Q;
        fail[root] = root;
        for (int i = 0; i < A; ++i) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            } else {
                fail[ next[root][i] ] = root;
                Q.push( next[root][i] );
            }
        }
        while (Q.size()) {
            int now = Q.front();
            Q.pop();
            if (end[ fail[now] ]) end[now]++;
            for (int i = 0; i < A; ++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]);
                }
            }
        }
    }

    ull query(ull n)
    {
        mat F(L+1, vec(L+1));
        for (int i = 0; i <= L; ++i) {
            for (int j = 0; j <= L; ++j) {
                F[i][j] = 0;
            }
        }
        for (int i = 0; i <= L; ++i) F[i][L] = 1;
        for (int i = 0; i < L; ++i) {
            for (int j = 0; j < A; ++j) {
                int nt = next[i][j];
                if (!end[nt]) F[i][nt]++;
            }
        }
        F = pow(F, n+1);
        return F[0][L] - 1;
    }

} ac;

char aff[10];
int main()
{
    int n;
    ull m;
    while (~scanf("%d%llu", &n, &m)) {
        ac.init();
        while (n--) {
            scanf("%s", aff);
            ac.insert(aff);
        }
        ac.build();
        ull ans1 = ac.query(m);
        mat F(2, vec(2));
        mat S(2, vec(1)); S[0][0] = 26; S[1][0] = 0;
        F[0][0] = 26; F[0][1] = 0;
        F[1][0] = 1;  F[1][1] = 1;
        F = pow(F, m);
        S = mul(F, S);
        ull ans2 = S[1][0];
        printf("%llu\n", ans2 - ans1);
    }
    return 0;
}

POJ1625

同poj2778,不过没有取模操作,需要大数。

大数不可以矩阵快速幂吗?内存不够……

使用dp递推,递推公式也比较简单。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <map>

using namespace std;

typedef vector<int> vec;
typedef vector<vec> mat;
const int N = 110;
const int A = 256;

map<char, int> mp;

struct BigInt
{
    const static int mod = 10000;
    const static int DLEN = 4;
    int a[600],len;
    BigInt()
    {
        memset(a,0,sizeof(a));
        len = 1;
    }
    BigInt(int v)
    {
        memset(a,0,sizeof(a));
        len = 0;
        do
        {
            a[len++] = v%mod;
            v /= mod;
        }while(v);
    }
    BigInt(const char s[])
    {
        memset(a,0,sizeof(a));
        int L = strlen(s);
        len = L/DLEN;
        if(L%DLEN)len++;
        int index = 0;
        for(int i = L-1;i >= 0;i -= DLEN)
        {
            int t = 0;
            int k = i - DLEN + 1;
            if(k < 0)k = 0;
            for(int j = k;j <= i;j++)
                t = t*10 + s[j] - ‘0‘;
            a[index++] = t;
        }
    }
    BigInt operator +(const BigInt &b)const
    {
        BigInt res;
        res.len = max(len,b.len);
        for(int i = 0;i <= res.len;i++)
            res.a[i] = 0;
        for(int i = 0;i < res.len;i++)
        {
            res.a[i] += ((i < len)?a[i]:0)+((i < b.len)?b.a[i]:0);
            res.a[i+1] += res.a[i]/mod;
            res.a[i] %= mod;
        }
        if(res.a[res.len] > 0)res.len++;
        return res;
    }
    BigInt operator *(const BigInt &b)const
    {
        BigInt res;
        for(int i = 0; i < len;i++)
        {
            int up = 0;
            for(int j = 0;j < b.len;j++)
            {
                int temp = a[i]*b.a[j] + res.a[i+j] + up;
                res.a[i+j] = temp%mod;
                up = temp/mod;
            }
            if(up != 0)
                res.a[i + b.len] = up;
        }
        res.len = len + b.len;
        while(res.a[res.len - 1] == 0 &&res.len > 1)res.len--;
        return res;
    }
    void output()
    {
        printf("%d",a[len-1]);
        for(int i = len-2;i >=0 ;i--)
            printf("%04d",a[i]);
        printf("\n");
    }
};

struct ACAutomata {

    int next[N][A], fail[N], end[N];
    int root, L;

    int idx(char ch)
    {
        return mp[ch];
    }
    int newNode()
    {
        for (int i = 0; i < A; ++i) next[L][i] = -1;
        end[L] = 0;
        return L++;
    }
    void init()
    {
        L = 0;
        root = newNode();
    }
    void insert(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        for (int i = 0; i < len; ++i) {
            int ch = idx(buf[i]);
            if (next[now][ch] == -1) next[now][ch] = newNode();
            now = next[now][ch];
        }
        end[now]++;
    }
    void build()
    {
        queue<int> Q;
        fail[root] = root;
        for (int i = 0; i < A; ++i) {
            if (next[root][i] == -1) {
                next[root][i] = root;
            } else {
                fail[ next[root][i] ] = root;
                Q.push( next[root][i] );
            }
        }
        while (Q.size()) {
            int now = Q.front();
            Q.pop();
            if (end[ fail[now] ]) end[now]++; //!!
            for (int i = 0; i < A; ++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]);
                }
            }
        }
    }
    mat query(int n)
    {
        mat F(L, vec(L));
        for (int i = 0; i < L; ++i) {
            for (unsigned j = 0; j < mp.size(); ++j) {
                int nt = next[i][j];
                if (!end[nt]) F[i][nt]++;
            }
        }
        return F;
    }

} ac;

char word[100];
BigInt dp[2][110];
int main()
{
    int n, m, p;
    while (~scanf("%d%d%d", &n, &m, &p)) {
        ac.init();
        getchar();
        gets(word);
        mp.clear();
        for (unsigned i = 0; i < strlen(word); ++i) mp[ word[i] ] = i;
        while (p--) {
            gets(word);
            ac.insert(word);
        }
        ac.build();
        mat F = ac.query(m);

        int now = 0;
        dp[now][0] = 1;
        for (int i = 1; i < ac.L; ++i) dp[now][i] = 0;
        for (int i = 1; i <= m; ++i) {
            now ^= 1;
            for (int j = 0; j < ac.L; ++j) dp[now][j] = 0;
            for (int j = 0; j < ac.L; ++j)
                for (int k = 0; k < ac.L; ++k)
                    if (F[j][k]) dp[now][k] = dp[now][k] + dp[now^1][j] * F[j][k];
        }
        BigInt res = 0;
        for (int i = 0; i < ac.L; ++i) res = res + dp[now][i];
        res.output();
    }
    return 0;
}
时间: 2024-10-20 22:44:19

POJ2778&HDU2243&POJ1625(AC自动机+矩阵/DP)的相关文章

[poj2778]DNA Sequence(AC自动机+矩阵快速幂)

解题关键:卡时限过的,正在找原因中. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstring> 6 #include<iostream> 7 #include<queue> 8 using namespace std; 9 typedef long long ll; 10 cons

hdu2243 ac自动机+矩阵连乘

http://acm.hdu.edu.cn/showproblem.php?pid=2243 Problem Description 背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般表示"相反,变坏,离去"等. 于是Lele想,如果背了N个词根,那这些词根到底会不会在单词里出现呢.更确切的描述是:长度不超过L,只由小写字母组成的,至少包

poj2778DNA Sequence(AC自动机+矩阵乘法)

链接 看此题前先看一下matrix67大神写的关于十个矩阵的题目中的一个,如下: 经典题目8 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值    把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j.令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点).类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数.同理,如果要求经过k步的路径数,我们只需要二分求出A^k

hdu 2243 AC自动机 + 矩阵快速幂

// hdu 2243 AC自动机 + 矩阵快速幂 // // 题目大意: // // 给你一些短串,问在长度不超过k的任意串,包含至少一个这些短串的其中 // 一个.问这样的串有多少个. // // 解题思路: // // 首先, 包含和不包含是一种互斥关系,包含+不包含 = 全集u.全集的答案就是 // 26 ^ 1 + 26 ^ 2 + .... + 26 ^ k.不包含的比较好求.构建一个自动机,得到 // 一个转移矩阵A.表示状态i能到状态j的方法数.而这些状态中都是不包含所给的 //

[AC自动机+概率dp] hdu 3689 Infinite monkey theorem

题意: 给n个字母,和m次数. 然后输入n个字母出现的概率 然后再给一个目标串str 然后问m次中敲出目标串的概率是多少. 思路: AC自动机+概率dp的简单题. 首先建立trie图,然后就是状态转移了 dp版本: dp三重循环变量次数,节点数,和字母数 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include"

HDU 2243 考研路茫茫――单词情结 (AC自动机 + 矩阵快速幂)

题目链接:考研路茫茫――单词情结 做本题前,个人建议先做一下POJ 2778 http://blog.csdn.net/u013446688/article/details/47378255 POJ2778 是求长度为n,不包含模式串的字符串个数. 而本题是求长度为n,包含模式串的字符串个数.直接用字符串总数减去不包含模式串的字符串个数即为所求. 同样是AC自动机 + 矩阵快速幂.但是还是有所不同的. 因为对2^64取模,所以定义数据类型为unsigned long long就可以了,这样就实现

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

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

POJ 3691 DNA repair 基于AC自动机的DP

dp[i][j] 表示长度为 i 的前缀到达第 j 个节点的最小更改数目. 很显然有dp[0][0] = 0; dp[ i ][ j ] = min(dp[ i ][ j ],dp[i-1][k] + (j == k ? 0 : 1)),当且仅当j,k满足下列条件时. j 不为某条模式串的末节点 且 j 到 root 的由失败指针组成的路径上无末节点. j 是k的儿子节点 或者 j 的父节点可由 k 沿着失败指针找到. #include <algorithm> #include <ios

spoj 1676 AC自动机+矩阵快速

Text Generator Time Limit: 1386MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Submit Status Description LoadingTime has been given a task these days. He is required to write a tool called Text Generator. This software is widely used amon