POJ 2778 DNA Sequence(AC自动机+矩阵)

转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove

题目:长度为N的DNA串不包含模式串的有多少个。

http://poj.org/problem?id=2778

N为20000000000,如此大的规模,肯定是lg(N)的算法,就会想到矩阵。

长度为10的模式串,10个,而且只有4个字母,最多不超过100个状态

建立AC自动机,记录里面的转移。

和基本的AC自动机有一点点不一样,如果某个结点的某个孩子结点为空,其它它还是可以转移的。

也就是他是可以存在后继状态,如果这个结点为根,那么还是转移到根,否则就转向失败指针指向的孩子结点。

需要建立转移矩阵,使用静态Trie,便于建立矩阵,两个状态之间可以转移,相当于存在一条路径,最终要求的是从空开始路径长度为n的个数。

不知道为什么,把Trie删掉反而会RE。

另外会超int,需要用64位整数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100005
#define MOD 100000
#define LL long long
using namespace std;
struct Trie{
    Trie *next[4];
    Trie *fail;
    int kind,isword;
};
Trie *que[N],s[N];
int idx;
int id(char ch){
    if(ch=='A') return 0;
    else if(ch=='T') return 1;
    else if(ch=='C') return 2;
    return 3;
}
Trie *NewTrie(){
    //静态Trie
    Trie *tmp=&s[idx];
    for(int i=0;i<4;i++) tmp->next[i]=NULL;
    tmp->isword=0;
    tmp->fail=NULL;
    tmp->kind=idx++;
    return tmp;
}
void Insert(Trie *root,char *s,int len){
    Trie *tmp=root;
    for(int i=0;i<len;i++){
        if(tmp->next[id(s[i])]==NULL) tmp->next[id(s[i])]=NewTrie();
        tmp=tmp->next[id(s[i])];
    }
    tmp->isword=1;
}
void Bulid_fail(Trie *root){
    int tail=0,head=0;
    que[tail++]=root;
    root->fail=NULL;
    while(head<tail){
        Trie *tmp=que[head++];
        for(int i=0;i<4;i++){
            if(tmp->next[i]==NULL){
                if(tmp==root) tmp->next[i]=root;
                else tmp->next[i]=tmp->fail->next[i];
            }
            else{
                if(tmp==root) tmp->next[i]->fail=root;
                else{
                    tmp->next[i]->fail=tmp->fail->next[i];
                    if(tmp->next[i]->fail->isword)
                        tmp->next[i]->isword=1;
                }
                que[tail++]=tmp->next[i];
            }
        }
    }
}
char str[15];
struct Matrix{
    LL m[101][101];
}Init;
Matrix mult(Matrix m1,Matrix m2){
    Matrix ans;
    memset(ans.m,0,sizeof(ans.m));
    for(int i=0;i<idx;i++){
        for(int k=0;k<idx;k++){
            if(m1.m[i][k]==0) continue;
            for(int j=0;j<idx;j++){
                ans.m[i][j]+=(LL)m1.m[i][k]*m2.m[k][j];
                if(ans.m[i][j]>=MOD) ans.m[i][j]%=MOD;
            }
        }
    }
    return ans;
}
Matrix PowMod(Matrix m1,int n){
    Matrix ans;
    for(int i=0;i<idx;i++) for(int j=0;j<idx;j++) ans.m[i][j]=i==j;
    while(n){
        if(n&1) ans=mult(ans,m1);
        m1=mult(m1,m1);
        n>>=1;
    }
    return ans;
}
int main(){
    int m,n;
    while(scanf("%d%d",&m,&n)!=EOF){
        idx=0;
        Trie *root=NewTrie();
        for(int i=0;i<m;i++){
            scanf("%s",str);
            Insert(root,str,strlen(str));
        }
        Bulid_fail(root);
        memset(Init.m,0,sizeof(Init.m));
        for(int i=0;i<idx;i++){
            for(int j=0;j<4;j++){
                Trie *son=s[i].next[j];
                if(!son->isword&&!s[i].isword)
                    Init.m[i][son->kind]++;
            }
        }
        Init=PowMod(Init,n);
        LL ans=0;
        for(int i=0;i<idx;i++){
            ans+=Init.m[0][i];
            if(ans>=MOD) ans-=MOD;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}




  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值