zoukankan      html  css  js  c++  java
  • DNA Sequence POJ

    DNA Sequence  POJ - 2778 

    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

    题意:首先输入的是n和m,分别代表接下来会给你n个病毒串,问你长度为m的不包含病毒的串有多少种
    思路:上次写这道题还是半年前及爱情在家写的,当时尚神指导,轻松AC,然而今天奉老大之命前来复习,读题之后发现,蒙蒙哒~~
    首先我们在纸上手动模拟一下或许会对这个题的理解更深刻,我们构建一个AC自动机树,在简单题中,我们已经熟练掌握了建树以及构建fail指针的方式,这道题即是使用到了这个树的一些实际意义。
    我们在构建fail指针的时候,加了一部分代码即是
    if(en[fail[now]] != 0)
                {
                    en[now] = 1;
                }

    什么意思呢,我们先来看fail指针的意义,树上每个结点代表一个子串,一个结点的fail指针指向另一个结点,代表的是这个后者是前者的一个后缀,这个在之前应该就是了解的。

    然后如果en[fail[now]]!=0 即代表该节点的后缀包含一个病毒串,自然,这个串也是不可以使用的,于是这个点代表的串也是一个病毒串。病毒串是不可以出现的。

    接下来我们来讨论为什么AC树是一个状态转移树,每个结点代表的是一个子串,那么从一个串经过next数组到达另一个串,他的意义就是经过一步可以由状态i转换为状态j。有了这句话我们就可以根据现在构建出来的AC树可行的转移路径构建一个转移方程了,部分代码是

        void BuildMatrix()
        {
            memset(mm.m, 0, sizeof(mm.m));
            for(int i=0; i<l; i++)
            {
                for(int j=0; j<4; j++)
                {
                    if(nex[i][j]!=-1 && en[i]==0 && en[nex[i][j]]==0)
                    {
                        mm.m[i][nex[i][j]] ++;         ///mm数组记录的是从状态i到状态j一步可以到达的方案数  则长度为n即为n步可达 n次幂即可  从根节点出发求和
                    }
                }
            }
    //        for(int i=0; i<l; i++)
    //        {
    //            for(int j=0; j<l; j++)
    //            {
    //                printf("%d  " , mm.m[i][j]);
    //            }
    //            printf("
    ");
    //        }
        }///到这应该是没有错误的

    三个判断条件分别约束的是,状态i,j都存在,状态i代表的子串不是病毒串,状态j代表的子串不是病毒串。

    于是我们可以通过一步从状态i转移到状态j,矩阵记录的是转移的方案数,于是++;

    接下来回到我们的问题是要长度为m,即由根节点转移m步,于是对一步可到达矩阵求m次幂可得到m步可到达矩阵,再将第0行加起来,即是从根节点m步可以到达的状态

    代码如下:

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<queue>
    
    using namespace std;
    #define mod 100000
    
    int m, n;
    int cnt['Z'+1];
    
    struct MM
    {
        long long m[110][110];
    };
    
    MM mm;
    MM m1, m2;
    
    void init()
    {
        cnt['A'] = 0;
        cnt['T'] = 1;
        cnt['C'] = 2;
        cnt['G'] = 3;
    }
    
    MM mul(MM a, MM b, int siz)
    {
        MM c;
        memset(c.m, 0, sizeof(c.m));
        for(int k=0; k<siz; k++)
        {
            for(int i=0; i<siz; i++)
            {
                if(a.m[i][k]==0) continue;
                for(int j=0; j<siz; j++)
                {
                    if(b.m[k][j]==0) continue;
                    c.m[i][j] += (a.m[i][k]*b.m[k][j])%mod;
                    c.m[i][j] %= mod;
                }
            }
        }
        return c;
    }
    
    struct Trie
    {
        int nex[110][4];
        int en[110];
        int fail[110];
        int root, l;
    
        int NewNode()
        {
            for(int i=0; i<4; i++)
            {
                nex[l][i] = -1;
            }
            en[l] = 0;
            l++;
            return l-1;
        }
        void Init()
        {
            l = 0;
            root = NewNode();
        }
        void Insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for(int i=0; i<len; i++)
            {
                if(nex[now][cnt[buf[i]]] == -1)
                {
                    nex[now][cnt[buf[i]]] = NewNode();
                }
                now = nex[now][cnt[buf[i]]];
            }
            en[now] = 1;
        }
        void Build()
        {
            queue<int>Q;
            int now;
            fail[root] = root;
            for(int i=0; i<4; i++)
            {
                if(nex[root][i] == -1)
                {
                    nex[root][i] = root;
                }
                else
                {
                    fail[nex[root][i]] = root;
                    Q.push(nex[root][i]);
                }
            }
            while( !Q.empty() )
            {
                now = Q.front();
                Q.pop();
                if(en[fail[now]] != 0)
                {
                    en[now] = 1;
                }
                for(int i=0; i<4; i++)
                {
                    if(nex[now][i] == -1)
                    {
                        nex[now][i] = nex[fail[now]][i];
                    }
                    else
                    {
                        fail[nex[now][i]] = nex[fail[now]][i];
                        Q.push(nex[now][i]);
    //                    if(en[fail[nex[now][i]]] != 0)
    //                    {
    //                        en[nex[now][i]] = 1;///en数组的值为1就不能走 为1代表是一个串的结尾 就不能走 为什么这个字符的fail指向一个en[]不为0的就也要置成不为0呢
    //                        ///因为一个病毒串是这个串的后缀 那么这个串虽然不是病毒串 到那时后半部分含有病毒串
    //                        ///为什么只要考虑与他直接相连的字符的fail值就可以了呢 因为可以抽象的理解为 fail一定是在他的上面的层 一定不在同一层 因为如果在同一层 则这两个串是一样的 饿就好似同一个串
    //                        ///这种情况是不会发生的 同时build()函数是从根节点发出的 保证了上面层的en数组都已经更新完成 具有传递的特质 所以只要考虑直接连接的fail值即可
    //                    }
                    }
                }
            }
    //        for(int i=0; i<l; i++)
    //        {
    //            printf("%d  %d  ..
    " , en[i] , fail[i]);
    //        }
        }
        void BuildMatrix()
        {
            memset(mm.m, 0, sizeof(mm.m));
            for(int i=0; i<l; i++)
            {
                for(int j=0; j<4; j++)
                {
                    if(nex[i][j]!=-1 && en[i]==0 && en[nex[i][j]]==0)
                    {
                        mm.m[i][nex[i][j]] ++;         ///mm数组记录的是从状态i到状态j一步可以到达的方案数  则长度为n即为n步可达 n次幂即可  从根节点出发求和
                    }
                }
            }
    //        for(int i=0; i<l; i++)
    //        {
    //            for(int j=0; j<l; j++)
    //            {
    //                printf("%d  " , mm.m[i][j]);
    //            }
    //            printf("
    ");
    //        }
        }///到这应该是没有错误的
        void Solve()
        {
            MM ans;
            memset(ans.m, 0, sizeof(ans));
            for(int i=0; i<l; i++)
            {
                ans.m[i][i] = 1;    ///0步只能到达本身
            }
            while( n )
            {
                if(n%2==1)
                {
                    n--;
                    ans = mul(ans, mm, l);
    //                for(int i=0; i<l; i++)
    //                {
    //                    for(int j=0; j<l; j++)
    //                    {
    //                        printf("%d++", ans.m[i][j]);
    //                    }
    //                    printf("
    ");
    //                }
                }
                mm = mul(mm, mm, l);
    //            for(int i=0; i<l; i++)
    //            {
    //                for(int j=0; j<l; j++)
    //                {
    //                    printf("%d..", mm.m[i][j]);
    //                }
    //                printf("
    ");
    //            }
                n /= 2;
            }
            int res;
            res = 0;
            for(int i=0; i<l; i++)
            {
                res = (res+ans.m[0][i])%mod;
            }
            printf("%d
    ", res);
        }
    };
    
    Trie ac;
    
    void input()
    {
        char s[12];
        for(int i=0; i<m; i++)
        {
            scanf("%s", s);
            ac.Insert(s);
        }
    }
    
    int main()
    {
        init();
        while( scanf("%d%d", &m, &n) != EOF )
        {
            ac.Init();
            input();
            ac.Build();
            ac.BuildMatrix();
            ac.Solve();
        }
    
        return 0;
    }
  • 相关阅读:
    node-webkit 笔记
    CEF 相关资料
    输出重定向
    FindProcDLL::FindProc 和 KillProcDLL::KillProc,必须使用WPF x86编译出来的程序
    wpf xaml inlines
    Gradle 笔记
    Android手机的 storage
    SpringMVC 工作原理详解
    SpringMVC 面试题
    18、多线程 (线程安全、线程同步、等待唤醒机制、单例设计模式)
  • 原文地址:https://www.cnblogs.com/Flower-Z/p/9485637.html
Copyright © 2011-2022 走看看