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

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
mean that the animal may have a genetic disease. Until now scientists
have found several those segments, the problem is how many kinds of
DNA sequences of a species don’t contain those segments.

Suppose that DNA sequences of a species is a sequence that consist of
A, C, T and G,and the length of sequences is a given integer n.

Input

First line contains two integer m (0 <= m <= 10), n (1 <= n
<=2000000000). Here, m is the number of genetic disease segment, and n
is the length of sequences.

Next m lines each line contain a DNA genetic disease segment, and
length of these segments is not larger than 10.

Output

An integer, the number of DNA sequences, mod 100000.

Sample Input

4 3
AT
AC
AG
AA

Sample Output

36

思路

先吐槽一下,AC自动机做了3道水题,以为会的差不多了,结果这题的难度骤增,看了三天才有点眉目~果然还是太菜o(╥﹏╥)o

现在说一说题意,这个题题意是首先给你一个n和m,然后n行病毒序列,给出一个长度m,要求这个长m的序列里面不包含前面给出的病毒序列,问符合条件的长度为m的串一共有多少种。

然后思路是,先构造AC自动机,然后利用end来标记这个串是不是病毒串。

以一个例子来说明一下:{“ACG”,”C”}(参考:Poj 2778 [AC自动机,矩阵乘法]

首先构造字典树:

然后我们给他们编上号码,每个编号代表每一种状态,分别是:0 1 2 3 4

那么假设我们当前在根节点,现在只走一步,我们能到达哪些状态?一共有ATCG这四种可能:

  • 走A:走A的话,很明显会到达状态1(安全)
  • 走T:走T的话,根据我们构造的fail指针,根节点里面没有T这个儿子,所以又会回到A节点,所以走T会达到状态0(安全)
  • 走G:走G的话,和上面的T一样,还会回到状态0(安全)
  • 走C:走C的话,因为根节点有C儿子,所以走C会变成状态4(病毒,危险)

所以当n的值为1的时候,有3个安全的节点,所以我们的走法有3种

我们根据上面的走法,可以构建一个矩阵:

  • mat[i][j]:代表从状态i到状态j,只走一步,有几种走法

也就是上面的那张图,我们可以构建一个矩阵:

2 1 0 0 1
2 1 1 0 0
1 1 0 1 1
2 1 0 0 1
2 1 0 0 1

这是一个5*5的矩阵,行号和列号都是从0~4,代表从i状态到j状态只走一步有几种走法

现在我们要走m步,应该怎么办呢,我们矩阵构建的是走一步有几种走法,那么走m步只需要把这个矩阵乘以m次就可以了,因为m可能太大,因此这里要用到矩阵快速幂

题目上求的是,不能包含病毒序列的种数,那么我们上面构建的矩阵是包含病毒序列的,很明显不符合题意,那么我们只需要把有病毒的节点的行和列中的所有值变成0,这样就可以符合题意,例如上面构建的矩阵去除病毒序列后矩阵就变成了:

2 1 0 0 0
2 1 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

我们只需要算出最后的序列第一行的和对100000取模就可以了

代码

#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <sstream>
#include <cmath>
#include <iostream>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=110;
const int mod=100000;
struct Matrix
{
    int mat[N][N], n;
    Matrix() {}
    Matrix(int _n)
    {
        n = _n;
        mem(mat,0);
    }
    Matrix operator*(const Matrix &b) const
    {
        Matrix ret = Matrix(n);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                {
                    int tmp = (long long)mat[i][k] * b.mat[k][j] % mod;
                    ret.mat[i][j] = (ret.mat[i][j] + tmp) % mod;
                }
        return ret;
    }
};
Matrix mat_pow(Matrix a, int n)
{
    Matrix ret = Matrix(a.n);
    for (int i = 0; i < ret.n; i++)
        ret.mat[i][i] = 1;
    Matrix tmp = a;
    while (n)
    {
        if (n & 1)
            ret = ret * tmp;
        tmp = tmp * tmp;
        n >>= 1;
    }
    return ret;
}
struct dicTree
{
    int next[N][4],fail[N];
    bool end[N];
    int root,sz;
    int newnode()
    {
        for(int i=0; i<4; i++)
            next[sz][i]=-1;
        end[sz++]=0;
        return sz-1;
    }
    void init()
    {
        sz=0;
        root=newnode();
    }
    int getch(char ch)
    {
        if(ch=='A')return 0;
        if(ch=='C')return 1;
        if(ch=='G')return 2;
        if(ch=='T')return 3;
    }
    void insert(char *s)
    {
        int len=strlen(s);
        int now=root;
        for(int i=0; i<len; i++)
        {
            int to=getch(s[i]);
            if(next[now][to]==-1)
                next[now][to]=newnode();
            now=next[now][to];
        }
        end[now]=true;
    }
    void build()
    {
        queue<int>q;
        fail[root]=root;
        for(int i=0; i<4; i++)
        {
            if(next[root][i]==-1)
                next[root][i]=root;
            else
            {
                fail[next[root][i]]=root;
                q.push(next[root][i]);
            }
        }
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            if(end[fail[now]]==true)
                end[now]=true;
            for(int i=0; i<4; 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]);
                }
            }
        }
    }
    Matrix get_mat()
    {
        Matrix res=Matrix(sz);
        for(int i=0; i<sz; i++)
            for(int j=0; j<4; j++)
                if(end[next[i][j]]==false&&end[i]==false)
                    res.mat[i][next[i][j]]++;
        return res;
    }
};
dicTree ac;
char s[N];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        ac.init();
        for(int i=0; i<n; i++)
        {
            scanf("%s",s);
            ac.insert(s);
        }
        ac.build();
        Matrix a=ac.get_mat();
        a=mat_pow(a,m);
        int ans=0;
        for(int i=0; i<ac.sz; i++)
            ans=(ans+a.mat[0][i])%mod;
        printf("%d\n",ans);
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值