DNA Sequence POJ - 2778(AC自动机fail数组+矩阵快速幂)

题目链接:
POJ:http://poj.org/problem?id=2778
SCU:http://acm.scu.edu.cn/soj/problem.action?id=3030(多组输入)
一个是单组,另一个是多组输入。


题意:DNA序列由'A''C''G''T'四个元素组成。问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列。
解题思路:其实这道题在想到矩阵之前还挺难的…
我开始时也很纳闷,怎么这道题和矩阵扯上关系了呢?不急我们慢慢来…

  • 首先我们需要构建一个矩阵M[i][j] 储存从字典树上的i点到j点只走一步并且走的方式没有疾病有多少种方式。

为了完成矩阵M。我们需要对所有的疾病DNA序列构建前缀树。并记录每个病毒结尾时的位置。这时,我们就可以根据前缀树的性质,排除掉M中这些病毒结尾时的位置。
但还有些病毒没有被排除,比如病毒是'ACG''C' 那么'AC'也要被排除因为AC的fail指向C的。所以我们需要对这棵前缀树求其fail指针。
上述工作做完之后就可以开始求M了。对每一个点,判断是否可以走ACGT这四个方向。

  • 如果可以走,那么M[父亲节点][子结点]++
  • 如果不能走, M[0].mat[i][trie[fail[i]][k]]++。相当于借助失配指针查找可以走到的位置。

比如对于3 AC CA G这组病毒来说,其矩阵M就应该为

1 1 0 1 0 0
1 1 0 0 0 0
0 0 0 0 0 0
1 0 0 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0

现在我们得到了M矩阵,从i到j走一步有多少种方法。下面是我认为最难想到的…
DNA Sequence POJ - 2778(AC自动机fail数组+矩阵快速幂)_第1张图片
矩阵
没错,Mn就是从i到j走n步有多少种方法…那么我们对M使用矩阵快速幂即可。
最后我们再统计从Mn[0][0]加到Mn[0][tot]就可以了…


AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
//#pragma GCC optimize(3,"Ofast","inline")
#define LL long long
#define MT(a,b) memset(a,b,sizeof(a))
const int mod=100000;
const int maxn=2e6+10;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
struct node{
    LL mat[150][150];
}M[5];
char t[20][20];
int trie[500][30],fail[500],vis[500],tot;
char dir[200];
void add_trie(char *str){
    int len=strlen(str);
    int pos=0;
    for (int i=0;i<len;i++){
        if (!trie[pos][dir[str[i]]]) trie[pos][dir[str[i]]]=++tot;
        pos=trie[pos][dir[str[i]]];
    }
    vis[pos]++;
}
void get_fail(){
    queue<int>q;
    for (int i=0;i<4;i++){
        if(trie[0][i]){
            q.push(trie[0][i]);
            fail[trie[0][i]]=0;
        }
    }
    while (!q.empty()){
        int pos=q.front();
        q.pop();
        for (int i=0;i<4;i++){
            if (trie[pos][i]) {
                fail[trie[pos][i]]=trie[fail[pos]][i];
                if (vis[fail[trie[pos][i]]]) vis[trie[pos][i]]++;
                q.push(trie[pos][i]);
            } else trie[pos][i]=trie[fail[pos]][i];
        }
    }
}
void build_trie(int n){
    MT(trie,0);
    MT(vis,0);
    MT(fail,0);
    tot=0;
    dir['A']=0,dir['C']=1,dir['G']=2,dir['T']=3;
    for (int i=0;i<n;i++){
        scanf("%s",t[i]);
        add_trie(t[i]);
    }
    get_fail();
    for (int i=0;i<=tot;i++){
        if (vis[i]) continue;
        for (int k=0;k<4;k++){
            if (trie[i][k]){
                if (!vis[trie[i][k]])
                    M[0].mat[i][trie[i][k]]++;
            } else {
                if (trie[0][k]) M[0].mat[i][trie[0][k]]++;
                else M[0].mat[i][0]++;
            }
        }
    }
}
void mat_mul(int a,int b,int d){
    node tmp = node();
    for (int i=0;i<=tot;i++){
        for (int j=0;j<=tot;j++){
            for (int k=0;k<=tot;k++){
                tmp.mat[i][j]+=M[a].mat[i][k]*M[b].mat[k][j];
                tmp.mat[i][j]%=mod;
            }
        }
    }
    M[d]=tmp;
}
void mat_qpow(int n,int p){
    for (int i=0;i<=tot;i++){
        M[1].mat[i][i]=1;
    }
    while (p){
        if (p&1){
            mat_mul(n,1,1);
        }
        p>>=1;
        mat_mul(n,n,n);
    }
}
int main(){
    int n,L;
    while (~scanf("%d%d",&n,&L)){
        MT(M[0].mat,0);
        MT(M[1].mat,0);
        build_trie(n);
        int ans=0;
        mat_qpow(0,L);
        for (int i=0;i<=tot;i++){
            ans+=M[1].mat[0][i];
            ans=ans%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(DNA Sequence POJ - 2778(AC自动机fail数组+矩阵快速幂))