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

题目大意:

给出N个串,问在长度为L的所有串中,不包含任一已知串的个数有多少个。

思路分析:

已知一个矩阵A,A[i][j] 表示 节点i 到 节点 j 有一条变可以到达的方法数。

那么A^2 ,这个矩阵的 [i][j] 就代表这个节点 i 到节点 j 有两条边可以到达的方法数。

那么知道这个结论,我们要做的就是求一个节点到另外一个节点,要经过L条变(对应这长度为L的单词),而又要满足任意一条边都不能经过已知单词。

所以我们要用到ac自动机处理出所有已知的单词,在ac自动机上得到这个矩阵,使得任意两个节点之间都不是已知单词的结尾,我们才连一条边。

还要理解AC自动机的一点,就是在失配的时候,也就是要得到fail的时候,其实可以抽象成你fail的下一个节点就是这个节点的next节点。可以看成是一个单词。

那么也就要加上代码中标记上的那个注释上的判断。可以稍微想一想。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 105
#define maxn 10005
using namespace std;
typedef long long ll;
const int mod = 100000;
int rev[180];

const char tab = 0;
const int max_next = 4;

int next[maxn][max_next],fail[maxn],num[maxn],siz;

int newnode()
{
    for(int i=0;i<max_next;i++)
        next[siz][i]=0;
    fail[siz]=num[siz]=0;
    return siz++;
}
void init()
{
    siz=0;
    newnode();
}
void Insert(char *s,int len)
{
    int p=0;
    for(int i=0;i<len;i++)
    {
        int &x=next[p][rev[s[i]]];
        p=x?x:x=newnode();
    }
    num[p]++;
}

void acbuild()
{
    queue<int>Q;
    Q.push(0);
    while(!Q.empty())
    {
        int temp=Q.front();
        Q.pop();
        for(int i=0;i<max_next;i++)
        {
            int v=next[temp][i];
            if(v==0)next[temp][i]=next[fail[temp]][i];
            else Q.push(v);
            if(temp!=0)fail[v]=next[fail[temp]][i];
            if(num[next[fail[temp]][i]])num[next[temp][i]]++;//---------
        }
    }
}

struct matrix
{
    int r,c;
    ll data[N][N];
    matrix(){}
    matrix(int _r,int _c):r(_r),c(_c){}
    friend matrix operator * (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=B.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<B.c;j++)
            {
                for(int k=0;k<A.c;k++)
                {
                    if(A.data[i][k] && B.data[k][j]){
                        res.data[i][j]+=A.data[i][k]*B.data[k][j];
                        res.data[i][j]%=mod;
                    }
                }
            }
        }
        return res;
    }
    friend matrix operator + (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<A.c;j++)
            {
                res.data[i][j]=A.data[i][j]+B.data[i][j];
                res.data[i][j]%=mod;
            }
        }
        return res;
    }
    friend matrix operator - (const matrix A,const matrix B)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)
        {
            for(int j=0;j<A.c;j++)
            {
                res.data[i][j]=A.data[i][j]-B.data[i][j];
                res.data[i][j]=(res.data[i][j]%mod+mod)%mod;
            }
        }
        return res;
    }
    friend matrix operator ^ (matrix A,int n)
    {
        matrix res;
        res.r=A.r;res.c=A.c;
        memset(res.data,0,sizeof res.data);
        for(int i=0;i<A.r;i++)res.data[i][i]=1;

        while(n)
        {
            if(n&1)res=res*A;
            A=A*A;
            n>>=1;
        }
        return res;
    }
    void print()
    {
        for(int i=0;i<r;i++)
        {
            for(int j=0;j<c;j++)
                printf("%d ",data[i][j]);
            puts("");
        }
    }
}E,zero;
char word[10005];
int main()
{
    for(int i=0;i<N;i++)
        E.data[i][i]=1;
    rev['A']=0;
    rev['C']=1;
    rev['G']=2;
    rev['T']=3;
    int n,L;
    while(scanf("%d%d",&n,&L)!=EOF)
    {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",word);
            Insert(word,strlen(word));
        }
        acbuild();
        matrix origin(siz,siz);
        for(int i=0;i<siz;i++)
        {
            if(!num[i])
            {
                for(int d=0;d<4;d++)
                {
                    if(!num[next[i][d]])
                    {
                        origin.data[i][next[i][d]]++;
                    }
                }
            }
        }
        origin= origin^L;
        int ans=0;
        for(int i=0;i<siz;i++)
        {
            if(!num[i])
                ans=(ans+origin.data[0][i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-01 05:20:08

Poj 2778 DNA Sequence (AC自动机+矩阵)的相关文章

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 ~ HDU - 2243 AC自动机+矩阵快速幂

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

[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

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自动机确定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

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

距离上次做AC自动机有很久了=.=,以前这题的思路死活看不懂,现在还是觉得很好理解的. 思路参见:http://blog.csdn.net/morgan_xww/article/details/7834801#. 我用cnt=1表示这个节点是危险的,然后再匹配fail指针的时候,如果一个节点的前缀是危险的,那么这个节点也是危险的,这么维护即可. 顺便一提,我以前的AC自动机模板是没有build过程中失配时的nxt指针的(以前是在match的过程中体现),但是失败时候需要的nxt指针又是很好用的,

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