POJ - 2778 DNA Sequence (AC自动机,求可以构造出来多少串.不包含原串,原串有10个左右,)

链接: https://vjudge.net/problem/POJ-2778

总共有101 个节点,可以建一个101*101的矩阵. 

a[i][j] 代表 从节点 i 走到 节点 j 的方案数. 

每走一步,都会形成一个后缀.就需要这个后缀不能包含给定的串,

且在构造的串中,加上一个字符不能达到给定的串.

如何构造矩阵呢.,

就是从 i 节点,然后加上任意一个字符可以到达的节点 j, 那么就在 a[i][j] 加一, 我们加的字符,到达 j,新形成的字符串不能是给定字符串的结尾,这个判断一下就好了. 

0 就是根节点,  加上一个字符,它或许可以走到下面的节点,那就走到 j, 如果下面没有这样的字符,我们就算走到 0 这个节点,重新匹配.

 

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 10000;
int idx[200],cnt;
queue<int>q;
struct Aho_Corasick_Automaton{
    int c[N][26],fail[N];
    bool vis[N];
    void init(){
        memset(vis, 0, sizeof vis);
        memset(c, 0, sizeof c);
        cnt = 0;
    }
    void ins(char *s){
        int len=strlen(s);int now=0;
        for(int i=0;i<len;i++){
            int v = idx[(int)s[i]];
            if(!c[now][v])c[now][v]=++cnt;
            now=c[now][v];
        }
        vis[now] = 1;
    }
    void build(){
        memset(fail, 0, sizeof fail);
        for(int i=0;i<4;i++)
            if(c[0][i])q.push(c[0][i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            vis[u] |= vis[fail[u]];
            for(int i=0;i<4;i++){
                if(c[u][i])fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
                else c[u][i]=c[fail[u]][i];
            }
        }
    }
}AC;

int n,m;
char p[50];
struct node{
    long long a[200][200];
};


node mul(node A, node B){
    node C; 
    for (int i = 0; i <= cnt; ++i)
        for(int j = 0; j <= cnt; ++j)
            C.a[i][j] = 0;
    for (int i = 0; i <= cnt; ++i)
        for (int j = 0; j <= cnt; ++j)
            for (int k = 0; k <= cnt; ++k)
                C.a[i][j] = (C.a[i][j] + 1ll*A.a[i][k] * B.a[k][j]) % 100000;
    return C;
}
node pow(node A, int k){
    node B;
    for (int i = 0; i <= cnt; ++i)
        for (int j = 0; j <= cnt; ++j)
            B.a[i][j] = 0;
    for (int i = 0; i <= cnt; ++i) B.a[i][i] = 1;
    while(k){
        if (k & 1) B = mul(B,A);
        A = mul(A,A);
        k >>= 1;
    }
    return B;
}

int main(){
    idx['A'] = 0; idx['C'] = 1; idx['T'] = 2; idx['G'] = 3;
    AC.init();
    scanf("%d%d",&m,&n);
    for (int i = 0; i < m; ++i){
        scanf("%s",p);
        AC.ins(p);
    }
    AC.build();
    node A;
    for (int i = 0; i <= cnt; ++i)
        for (int j = 0; j <= cnt; ++j)
            A.a[i][j] = 0;

    for (int i = 0; i <= cnt; ++i){
        if (AC.vis[i]) continue;
        for (int j = 0; j < 4; ++j){
            if (AC.vis[AC.c[i][j]]) continue;
            A.a[i][AC.c[i][j]] ++;
        }
    }
    for (int i = 0; i <= cnt; ++i){
        for (int j = 0; j <= cnt; ++j)
            printf("%d ",A.a[i][j]);
        printf("\n");
    }
    A = pow(A,n);
    long long ans = 0;
    for (int i = 0; i <= cnt; ++i)
        ans = (ans + A.a[0][i])% 100000;
    printf("%lld\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值