POJ 2778 DNA Sequence(AC自动机确定DFA转移图+矩阵快速幂)

这道题极好的展示了AC自动机在构造转移图DFA上的应用

DFA转移图就是展示状态的转移过程的图,DFA图构造出来后就可以用DP求出任何DNA长度下,任何状态的个数

本题用自动机求出DFA矩阵,那么有

| dp[n][0] dp[n][1] ... dp[n][m] |=|dp[1][0] dp[1][1] ... dp[1][m] | * DFA^(n-1)    (m指状态总数)

DP边界矩阵|dp[1][0] dp[1][1] ... dp[1][m] | 也就是DFA的第一行,所以dp[n]矩阵就是DFA^n

可见用自动机求某类状态转移方程不仅思路简单,而且可以用矩阵快速幂加速状态转移,复杂度log N

我是通过这篇学会的http://hi.baidu.com/ccsu_010/item/7847a3c17f6fe2bc0d0a7b89 ,感谢

//Accepted	796K	360MS	C++	3171B
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
const int M = 110;
const int mod = 100000;
struct mat
{
    ll a[M][M];
    mat()
    {
        memset(a,0,sizeof(a));
    }
};
mat DFA;

struct node
{
    node *son[4];
    node *fail;
    bool flag;
    int id;
}trie[M],*que[M],*root;

int Index(char x)
{
    switch(x)
    {
        case 'A' : return 0;
        case 'C' : return 1;
        case 'G' : return 2;
        case 'T' : return 3;
    }
}
struct AC
{
    int head,tail,sz;
    node *createnode()
    {
        trie[sz].flag=0;
        memset(trie[sz].son,0,sizeof(trie[sz].son));
        trie[sz].fail=NULL;
        trie[sz].id=sz;
        return &trie[sz++];
    }

    void ini()
    {
        head=tail=0;
        sz=0;
        root=createnode();
    }
    void Insert(char str[])
    {
        node *cur=root;
        for(int i=0;str[i];i++)
        {
            int val=Index(str[i]);
            if(cur->flag) break;
            // 如果当前插入的已经是危险片段那么就可以不用继续插入了(针对此题而言)
            //要继续插入也可以不过它的后面全要标记成危险片段if(cur->flag) cur->son[val]->flag=true;

            if(cur->son[val]==NULL)
            {
                cur->son[val]=createnode();
            }

            cur=cur->son[val];

        }
        cur->flag=true;
    }
    void acfun()
    {
        que[head++]=root;
        while(tail<head)
        {
            node *cur=que[tail++];
            for(int i=0;i<4;i++)
            {
                if(cur->son[i]!=NULL)
                {
                    if(cur==root) cur->son[i]->fail=root;
                    else cur->son[i]->fail=cur->fail->son[i];
                    if(cur->son[i]->fail->flag) cur->son[i]->flag=true; //易遗漏
                    que[head++]=cur->son[i];
                }
                else
                {
                    if(cur==root) cur->son[i]=root;
                    else cur->son[i]=cur->fail->son[i];
                }
            }
        }
    }

}ac;
mat mul(mat m1,mat m2)
{

    mat ans;
    for(int i=0;i<ac.sz;i++)
    for(int j=0;j<ac.sz;j++)
        if(m1.a[i][j])
        for(int k=0;k<ac.sz;k++)
        {
            ans.a[i][k]=(ans.a[i][k]+m1.a[i][j]*m2.a[j][k])%mod;
        }
    return ans;
}
int quickmul(mat m,int n)
{
    mat ans;
    for(int i=0;i<ac.sz;i++) ans.a[i][i]=1;
    while(n)
    {
        if(n&1) ans=mul(ans,m);
        m=mul(m,m);
        n>>=1;
    }
    ll sum=0;
    for(int i=0;i<ac.sz;i++)
    {
        sum+=ans.a[0][i];
    }
    return sum%mod;
}

void ini(){
    ac.ini();
    memset(DFA.a,0,sizeof(DFA.a));
}
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        ini();
        while(m--)
        {
            char tmp[15];
            scanf("%s",tmp);
            ac.Insert(tmp);
        }
        ac.acfun();
        for(int i=0;i<ac.sz;i++)
        {
            if(!trie[i].flag)
            {
                for(int j=0;j<4;j++){
                    if(!trie[i].son[j]->flag)
                        DFA.a[i][trie[i].son[j]->id ]++;
                }
            }
        }
        printf("%d\n",quickmul(DFA,n));
    }
}
时间: 2024-10-17 20:27:08

POJ 2778 DNA Sequence(AC自动机确定DFA转移图+矩阵快速幂)的相关文章

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就

poj 2778 DNA Sequence(AC自动机+矩阵快速幂)

题目链接:poj 2778 DNA Sequence 题目大意:给定一些含有疾病的DNA序列,现在给定DNA长度,问有多少种不同的DNA序列是健康的. 解题思路:对DNA片段建立AC自动机,因为最多10个串,每个串最长为10,所以最多可能有100个节点,在长度为n时 以每个节点终止的健康字符串个数形成一个状态集,通过AC自动机形成的边可以推导出n+1的状态集,走到单词节点是 非法的,所以同样的我们可以先走到单词节点,但是从单词节点不向后转移.这样可以构造一个矩阵,剩下的就是矩阵 快速幂.注意的一

POJ 2778 DNA Sequence (AC自动机,矩阵乘法)

题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<string> 6 #include<algorithm> 7 #include<queue> 8 #defin

Poj 2778 DNA Sequence (AC自动机+矩阵)

题目大意: 给出N个串,问在长度为L的所有串中,不包含任一已知串的个数有多少个. 思路分析: 已知一个矩阵A,A[i][j] 表示 节点i 到 节点 j 有一条变可以到达的方法数. 那么A^2 ,这个矩阵的 [i][j] 就代表这个节点 i 到节点 j 有两条边可以到达的方法数. 那么知道这个结论,我们要做的就是求一个节点到另外一个节点,要经过L条变(对应这长度为L的单词),而又要满足任意一条边都不能经过已知单词. 所以我们要用到ac自动机处理出所有已知的单词,在ac自动机上得到这个矩阵,使得任

POJ 2778 DNA Sequence (AC自动机 + 矩阵快速幂)

题目链接:DNA Sequence 解析:AC自动机 + 矩阵加速(快速幂). 这个时候AC自动机 的一种状态转移图的思路就很透彻了,AC自动机就是可以确定状态的转移. AC代码: #include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; const int MOD = 100000; struct Matrix{ int ma

POJ 2778 DNA Sequence (矩阵快速幂 + AC自动鸡)

题目:传送门 题意: 给你m个病毒串,只由(A.G.T.C) 组成, 问你生成一个长度为 n 的 只由 A.C.T.G 构成的,不包含病毒串的序列的方案数. 解: 对 m 个病毒串,建 AC 自动机, 然后, 这个AC自动机就类似于一张有向图, 可以用邻接矩阵存这张有向图. 最多10个病毒串, 每个病毒串长度不超过 10, 那最多是个 100 * 100 的矩阵, 可以接受. 最后用矩阵快速幂加速推导. #include<cstdio> #include<cstring> #inc

POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂

这两题属于AC自动机的第二种套路通过矩阵快速幂求方案数. 题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的. 根据AC自动机的tire图,我们可以获得一个可达矩阵. 关于这题的tire图详解可以点击这里,往下面翻,这个博主的图对于tire图讲的非常详细. 知道了什么是tire图,理解了tire图后,后面的AC自动机的题目才能写. AC自动机的灵魂应该就是tire图 然后问题就变成了,得到了一个可达矩阵后,如何求方案数呢? 这个n = 2000000000 这咋办呢? 给定一

POJ 2778 DNA Sequence ( Trie图、矩阵快速幂 )

题意 : 给出一些病毒串,问你由ATGC构成的长度为 n 且不包含这些病毒串的个数有多少个 分析: 我们先分析Tire 图的结构 : Trie图是在AC自动机的原型上增添边使得状态可以快速转移,标记危险的节点(后缀是不良单词的节点): 那我们是想构造长度是n不包含不良串对不对 , 那是不是在trie图上从0节点走n步到安全节点的方案数(Trie图也是状态转移图) 在一个有向图中,A走k步到B的方案数(这显然是经典的矩阵快速幂问题),(原理需要自己搜索)先对原图建立一个邻接表M[i][j] , M

poj 3744 Scout (Another) YYF I - 概率与期望 - 动态规划 - 矩阵快速幂

(Another) YYF is a couragous scout. Now he is on a dangerous mission which is to penetrate into the enemy's base. After overcoming a series difficulties, (Another) YYF is now at the start of enemy's famous "mine road". This is a very long road,