POJ2778 DNA Sequence Trie+矩阵乘法

题意:给定N个有A C G T组成的字符串,求长度为L的仅由A C G T组成的字符串中有多少个是不含给定的N个字符串的题解:

首先我们把所有的模式串(给定的DNA序列)建Trie,假定我们有一个匹配串,并且在匹配过程到S[i]这个字符时匹配到了Trie上的某个节点t,那么有两种可能:

匹配失败:t->child[S[i]]为空,跳转到t->fail,因此t->fail一定不能是某个模式串的结尾;

匹配成功:跳转到t->child[S[i+1]],因此t->child[S[i+1]]一定不能是某个模式串的结尾。

另外还有一个性质:如果t->fail是某个模式串的结尾,那么t也被视作某个模式串的结尾(t->fail这个模式串为当前匹配到的位置的子串)

由于模式串最多有10个,每个模式串最长就是10,因此Trie上最多有100个节点,所以我们将Trie上的每个节点编号,构造初始矩阵a[i][j]为标号i走一步到标号j的方案数(注意Tire上的边都是有向的),能不能走由上面的规则决定。

因此问题转变为从根节点出发走n步不经过某个模式串的结尾的节点到达任意节点的方案总数,矩阵乘法随便做。

#include <map>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long

const int P=100000;
const int MAXK=4;
const int MAXN=100+2;
struct Trie{
    int mark;
    Trie *child[MAXK],*fail;
    bool flag;
}*root,*mark[MAXN];
int N,M,cnt;
ll a[MAXN][MAXN],b[MAXN][MAXN],t[MAXN][MAXN],ans;
char S[MAXN];
queue<Trie *> q;
map<char,int> m;

Trie *NewNode(int k){
    Trie *x=new Trie;
    memset(x,0,sizeof(Trie));
    x->mark=k,mark[k]=x;
    return x;
}

void Insert(Trie *&x,char *S){
    Trie *p=x;
    for(int i=0;S[i];i++){
        if(!p->child[m[S[i]]]) p->child[m[S[i]]]=NewNode(++cnt);
        p=p->child[m[S[i]]];
    }
    p->flag=1;
}

void Get_Matrix(Trie *&x){
    for(int i=0;i<MAXK;i++)
        if(x->child[i]) x->child[i]->fail=root,q.push(x->child[i]);
        else x->child[i]=x;

    Trie *p,*t;
    while(!q.empty()){
        t=q.front(),q.pop();
        if(t->fail->flag) t->flag=1;
        for(int i=0;i<MAXK;i++)
            if(t->child[i]){
                t->child[i]->fail=t->fail->child[i];
                q.push(t->child[i]);
            }
            else t->child[i]=t->fail->child[i];
    }

    for(int i=1;i<=cnt;i++)
        for(int j=0;j<MAXK;j++)
            if(!mark[i]->flag && !mark[i]->child[j]->flag) a[mark[i]->mark][mark[i]->child[j]->mark]++;
}

void Matrix_Copy(ll a[MAXN][MAXN],ll b[MAXN][MAXN]){
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            a[i][j]=b[i][j];
}

void Matrix_Mul(ll a[MAXN][MAXN],ll b[MAXN][MAXN]){
    memset(t,0,sizeof(t));
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            for(int k=1;k<=cnt;k++)
                t[i][j]=(t[i][j]+a[i][k]*b[k][j])%P;
    Matrix_Copy(a,t);
}

void Quick_Pow(ll x[MAXN][MAXN],int y,ll ans[MAXN][MAXN]){
    if(y&1) Matrix_Copy(ans,x);
    else
        for(int i=1;i<=cnt;i++) ans[i][i]=1;

    while(y>>=1){
        Matrix_Mul(x,x);
        if(y&1) Matrix_Mul(ans,x);
    }
}

int main(){
    m[‘A‘]=0,m[‘G‘]=1,m[‘C‘]=2,m[‘T‘]=3;
    root=NewNode(++cnt);

    cin >> M >> N;
    for(int i=1;i<=M;i++){
        cin >> S;
        Insert(root,S);
    }
    Get_Matrix(root);

    Quick_Pow(a,N,b);
    for(int i=1;i<=cnt;i++) ans=(ans+b[1][i])%P;

    cout << ans << endl;

    return 0;
}

时间: 2024-10-06 05:02:46

POJ2778 DNA Sequence Trie+矩阵乘法的相关文章

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

[POJ2778]DNA Sequence

看到n<=20亿顿时傻眼..AC自动机上用矩阵乘法优化DP...sxbk 建出AC自动机,把非法的节点去掉后求出trie图... 然后根据trie图中的转移关系建矩阵....最后跑个快速幂 竟然搞出来了...感人肺腑 脑子各种短路..先是矩乘打挂,然后是trie图求措TAT.调了一整节晚自修. 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm>

【AC自动机】【矩阵乘法】poj2778 DNA Sequence

http://blog.csdn.net/morgan_xww/article/details/7834801 讲得很好~可以理解自动机的本质,就是一个用来状态转移的东西~对于确定的输入而言,可以从初始状态,按照转移边,转移到确定的终止状态. 而这种转移可以用矩乘加速. #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<iostream> us

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

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

[POJ2778]DNA Sequence(AC自动机 + DP + 矩阵优化)

传送门 AC自动机加DP就不说了 注意到 m <= 10,所以模式串很少. 而 n 很大就需要 log 的算法,很容易想到矩阵. 但是该怎么构建? 还是矩阵 A(i,j) = ∑A(i,k) * A(k,j),那么i到j的方案数就是j到k的方案数称k到j的方案数,那么直接矩阵快速幂即可 #include <queue> #include <cstdio> #include <cstring> #define N 100001 #define p 100000 #d

[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

POJ2778 DNA Sequence AC自动机上dp

网址:https://vjudge.net/problem/POJ-2778 题意: 给出字符集${A,C,G,T}$和一些字符串(长度不超过$10$,且数量不超过$10$个),求长度为$n(n \leq 2e9)$的字符串中不包括上面这些字符串的字符串的数量. 题解: 我们可以先考虑一种方式:设$dp(i,j)$是用了$i$个字符拼出符合题意的长度为$j$的字符串的数量,在本题中$dp(i,j)=\sum _{j' \subseteq j} dp(i-1,j')$,显然时间复杂度是指数级的,不

【HDOJ5950】Recursive sequence(矩阵乘法,快速幂)

题意:f[1]=a,f[2]=b,f[i]=2f[i-2]+f[i-1]+i^4(i>=3),多组询问求f[n]对2147493647取模 N,a,b < 2^31 思路:重点在于i^4的处理 对于i转移矩阵中可以记录下它的0,1,2,3,4次项 i的幂又可以由i-1的幂运算得出,最后推出的系数是二项式展开的系数 试试新的矩乘模板 1 #include<cstdio> 2 #include<cstring> 3 #include<string> 4 #inc

DNA Sequence(POJ2778 AC自动机dp+矩阵加速)

传送门 DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K       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 sequenc