POJ 2778. DNA Sequence

链接

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

题意

字符集为 ACTG,有 m m m ( m ≤ 10 ) (m\le10) (m10) 个长度小于等于 10 10 10 的模式串,求长度为 n n n 且不包含这些模式串的字符串个数。

思路

建立 AC 自动机,判断每个节点的后缀是否是模式串。

d p i , j dp_{i,j} dpi,j 为在 trie 树上从根节点出发走了 i i i 步到达节点 j j j 且没有经过模式串的方案数,等价于长度为 i i i 以节点 j j j 代表的字符串为后缀且没有经过模式串的字符串个数。

a i , j a_{i,j} ai,j 为从 trie 树上节点 i i i 走一步到达节点 j j j 的方案数,trie 树除根节点外有 k k k 个节点:

d p i , j = d p i − 1 , 0 ∗ a 0 , j + d p i − 1 , 1 ∗ a 1 , j + ⋯ + d p i − 1 , k ∗ a k , j ⟹ [ d p i , 0 d p i , 1 ⋯ d p i , k ] = [ d p i − 1 , 0 d p i − 1 , 1 ⋯ d p i − 1 , k ] ∗ [ a 0 , 0 a 0 , 1 ⋯ a 0 , k a 1 , 0 a 1 , 1 ⋯ a 1 , k ⋮ ⋮ ⋱ ⋮ a k , 0 a k , 1 ⋯ a k , k ] \begin{aligned} & dp_{i,j}=dp_{i-1,0}*a_{0,j}+dp_{i-1,1}*a_{1,j}+\cdots+dp_{i-1,k}*a_{k,j} \\ \Longrightarrow & \begin{bmatrix} dp_{i,0} & dp_{i,1} & \cdots & dp_{i,k} \end{bmatrix}= \begin{bmatrix} dp_{i-1,0} & dp_{i-1,1} & \cdots & dp_{i-1,k} \\ \end{bmatrix}* \begin{bmatrix} a_{0,0} & a_{0,1} & \cdots & a_{0,k} \\ a_{1,0} & a_{1,1} & \cdots & a_{1,k} \\ \vdots & \vdots & \ddots & \vdots \\ a_{k,0} & a_{k,1} & \cdots & a_{k,k} \\ \end{bmatrix} \end{aligned} dpi,j=dpi1,0a0,j+dpi1,1a1,j++dpi1,kak,j[dpi,0dpi,1dpi,k]=[dpi1,0dpi1,1dpi1,k]a0,0a1,0ak,0a0,1a1,1ak,1a0,ka1,kak,k

[ d p i , 0 d p i , 1 ⋯ d p i , k ] \begin{bmatrix} dp_{i,0} & dp_{i,1} & \cdots & dp_{i,k} \end{bmatrix} [dpi,0dpi,1dpi,k] [ a 0 , 0 a 0 , 1 ⋯ a 0 , k a 1 , 0 a 1 , 1 ⋯ a 1 , k ⋮ ⋮ ⋱ ⋮ a k , 0 a k , 1 ⋯ a k , k ] i \begin{bmatrix} a_{0,0} & a_{0,1} & \cdots & a_{0,k} \\ a_{1,0} & a_{1,1} & \cdots & a_{1,k} \\ \vdots & \vdots & \ddots & \vdots \\ a_{k,0} & a_{k,1} & \cdots & a_{k,k} \\ \end{bmatrix}^{i} a0,0a1,0ak,0a0,1a1,1ak,1a0,ka1,kak,ki 的第一行。

最终答案为 ∑ i = 0 k d p n , i \sum_{i=0}^kdp_{n,i} i=0kdpn,i

代码

#include <iostream>
#include <cstring>
#include <queue>
#define SZ(x) (int)(x).size()
using namespace std;
typedef long long LL;
const LL MOD=100000;
const int N=15;
int n,m,tr[N*N][4],fail[N*N],st[N*N],cnt;
struct M {
    LL a[N*N][N*N];
    void clear() {memset(a,0,sizeof a);}
    M() {clear();}
    M operator * (const M &T) const {
        M ret;
        for(int i=0;i<=cnt;i++)
            for(int k=0;k<=cnt;k++) {
                LL t=a[i][k];
                if(!t) continue;
                for(int j=0;j<=cnt;j++) {
                    if(!T.a[k][j]) continue;
                    ret.a[i][j]=(ret.a[i][j]+T.a[k][j]*t%MOD)%MOD;
                }
            }
        return ret;
    }
    M operator ^ (LL b) const {
        M ret,bas;
        for(int i=0;i<=cnt;i++) ret.a[i][i]=1;
        for(int i=0;i<=cnt;i++)
            for(int j=0;j<=cnt;j++)
                bas.a[i][j]=a[i][j];
        while(b) {
            if(b&1) ret=ret*bas;
            bas=bas*bas;
            b>>=1;
        }
        return ret;
    }
}mat;
int get(char c) {
    if(c=='A') return 0;
    if(c=='C') return 1;
    if(c=='T') return 2;
    return 3;
}
void insert(const string &s) {
    int u=0;
    for(int i=0;s[i];i++) {
        int v=get(s[i]);
        if(!tr[u][v]) tr[u][v]=++cnt;
        u=tr[u][v];
    }
    st[u]=1;
}
void build() {
    queue<int> q;
    for(int i=0;i<4;i++) if(tr[0][i]) q.push(tr[0][i]);
    while(SZ(q)) {
        int u=q.front();
        q.pop();
        for(int v=0;v<4;v++) {
            if(tr[u][v]) fail[tr[u][v]]=tr[fail[u]][v],q.push(tr[u][v]);
            else tr[u][v]=tr[fail[u]][v];
        }
        st[u]|=st[fail[u]];
    }
}
void get_matrix() {
    for(int i=0;i<=cnt;i++) {
        if(st[i]) continue;
        for(int j=0;j<4;j++) {
            if(st[tr[i][j]]) continue;
            ++mat.a[i][tr[i][j]];
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>m>>n;
    for(int i=1;i<=m;i++) {
        string s;
        cin>>s;
        insert(s);
    }
    build();
    get_matrix();
    mat=mat^n;
    LL res=0;
    for(int i=0;i<=cnt;i++) res=(res+mat.a[0][i])%MOD;
    cout<<res<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值