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

题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的。

参考:http://www.matrix67.com/blog/archives/276

首先解决这个问题:给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值

    把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。



首先考虑长度为n的DNA可以由长度为n-1的加上一个字母得来,可以把长度为n的不含这些片段的DNA分成几类,并建立递推关系。
例如不含ATC,AAA,GGC,CT的DNA可以分成以下几类:
后缀是?A的、后缀是AT的、后缀是AA的、后缀是?G的、后缀是GG的、后缀是?C的,以及后缀是??的(?表示其他任意字母,这些类都是不重叠的,也就是说?A可以是AC,AT,AG,而不能是AA)之所以分成这些类是为了建立它们之间的递推关系,比如?A转化到AT有1种方法(后面加个T),转化到AA有1种方法(后面加个A),转化到?G有1种方法(后面加个G),转化到?C有1种方法(后面加个C),由于?A无论加哪个字母都能转化到一种情况,所以?A不能转化到??,也就是说我们可以求出每种情况与其他各种情况的转化关系,剩下的方法数就是转化到??的。最后我们要求的是从??转化到所有情况的方法数。
这些转化关系可以用AC自动机求出。由于AC自动机的fail指针可以指向与当前串后缀相同的最长字符串尾节结点,那么当前结点就可以转化到这个结点的后继结点。


上述做法的思想就是:从一个合法的字符串后缀,转移到另外一个合法的字符串后缀。如果存在这种转移,那么就在这两个状态之间连接一条边(建图)。然后问题就转化成了第一个提出的问题。


代码说明:

val值表示当前节点是否为病毒串(即后缀是否为病毒串)

建AC自动机的时候,将所有的失配节点的fail指针省略了,直接连接了一条边(因此将失配边和其他边等同看待了),最终的Trie树将变成一个有向图。

对于自动机代码的细节可以参考刘汝佳的《训练指南》。

注意自动机的两个注释。因为当前结点可能不是一个病毒串的终点,但是其失配边指向的串是个病毒,说明当前串的后缀也是个病毒串。




#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;

struct AC_Automata {
    #define Nn 102
    #define M 4
    int ch[Nn][M], val[Nn], f[Nn], last[Nn], sz;
    void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); }
    int idx(char c) {
        if (c == 'A') return 0;
        if (c == 'C') return 1;
        if (c == 'T') return 2;
        return 3;
    }

    void insert(char s[], int v) {
        int u = 0;
        for (int i=0; s[i]; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = 1;  ///标记当前节点是病毒串
    }
    void build() {
        queue q;
        f[0] = 0;
        for (int c=0; c>= 1;
    }
    copy(a, ret);
}
void init() {
    n = ac.sz;
    int u;
    memset(a, 0, sizeof(a));
    for (int i=0; i




你可能感兴趣的:(ACM,字符串匹配,数据结构,题解)