100道动态规划——28 POJ 2778 DNA Sequence AC自动机+DP+矩阵快速幂 很不错嘛

        一眼看出是AC自动机上的DP,因为数据量的问题显然不能记忆化搜索或者是递推,然后就不知道怎么做了

        天知道还要联系到矩阵快速幂,图论的东西好久都没写过了=_=

        定义一个Matrix的数据结构,也就是矩阵,由矩阵的长、宽、以及每个分量上的元素描述而成,支持通常意义上的矩阵乘法(在具体实现的时候用的是稀疏矩阵乘法),此外拥有一个成员函数qpow(int n),返回值为当前矩阵的n次幂。在这道题里用作Trie图的邻接矩阵。

        定义类型为Matrix数组dp[n],其中dp[k][i][j],表示从自动机上的i点,走了k步之后,到达j点的道路条数。

        因此最后的答案就是∑dp[n][0][i],0<=i<=最大节点

        实际上这题在建立一个AC自动机后,就和AC自动机关系不大了,就变成了一个图论上的计数问题

       为什么这样可行呢?暂且先不管AC自动机,就拿一张图来说,dp[k][i][j]表示在某一张图上走了k步之后,从i点到达j点的道路条数,那么dp[k]*dp[1]即为描述在图上走了k+1步后的邻接矩阵,即为dp[k+1]。注意dp[k]和dp[1]的类型都是Matrix,进行的是矩阵乘法。

        这道题在建立起AC自动机后,get到初始的邻接矩阵,开n次幂就好

        在get初始的邻接矩阵的过程中,需要注意的就是,从被禁止的节点出发到任何一个节点的道路条数都是0。

        被禁止的节点是指,题目中显式给出的串的最后一个字符对应的节点以及通过next(在代码里记做fail)指针指向禁止节点的节点

        至于为什么还要加上后面一条,这和AC自动机有关,因为next指向的必然是当前串的后缀,因此当前串也是不合法的(其实我更愿意把next指向的叫做自动机上当前状态的 等价状态)

        在整个求解的过程中有点递推的味道,只是这里的单位元是矩阵,而不是数。

        以邻接矩阵作为基本单元的递推,状态转移方程是dp[k+1]=dp[k]*dp[1]

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define ll long long

using namespace std;

const ll mod=100000,maxm=1E6+5;

struct Matrix{
    ll len,a[105][105];
    Matrix(ll l=1,bool identity=false):len(l){//identity表示该矩阵初始是全0还是单位矩阵,默认全0
        memset(a,0,sizeof a);
        if(identity)
        for(ll i=0;i<len;++i)
            a[i][i]=1;
    }
    Matrix& operator=(const Matrix& m){
        len=m.len;
        for(ll i=0;i<len;++i)
        for(ll j=0;j<len;++j)
            a[i][j]=m.a[i][j];
    }
    Matrix operator*(const Matrix& m)const{
        Matrix te(len);
        for(ll i=0;i<len;++i)
        for(ll j=0;j<len;++j)
        if(a[i][j])
        for(ll k=0;k<len;++k)
            te.a[i][k]+=a[i][j]*m.a[j][k],te.a[i][k]%=mod;
        return te;
    }
    Matrix qpow(int n){
        Matrix te(len,true),m=*this;
        while(n){
            if(n&1)
                te=te*m;
            m=m*m;
            n>>=1;
        }
        return te;
    }
    void print(){
        for(ll i=0;i<len;++i){
            for(ll j=0;j<len;++j)
                cout<<a[i][j]<<' ';
            cout<<endl;
        }
    }
};

inline int p(char a){if(a=='A')return 0;else if(a=='C')return 1;else if(a=='T')return 2;else return 3;}
char str[15];
ll n,sz,root,m,ch[maxm][4],fail[maxm],newnode();
void init(),getfail(),ins();
bool flag[maxm];

int main(){
    ios_base::sync_with_stdio(false);
    while(cin>>m>>n){
        init();
        for(int i=0;i<m;++i){
            cin>>str;
            ins();
        }

        getfail();
        Matrix te(sz);
        for(int i=0;i<sz;++i)
        if(!flag[i])
        for(int j=0;j<4;++j)
        if(!flag[ch[i][j]])
            te.a[i][ch[i][j]]++;

        Matrix tem=te.qpow(n);
        ll ans=0;
        for(int i=0;i<sz;++i)
        ans+=tem.a[0][i],ans%=mod;

        cout<<ans<<endl;
    }
    return 0;
}

ll newnode(){
    flag[sz]=0;
    memset(ch[sz],0,sizeof ch[sz]);
    return sz++;
}

void init(){
    sz=0;
    root=newnode();
}

void ins(){
    int u=root;
    for(int i=0;str[i];++i){
        if(ch[u][p(str[i])]==root)
            ch[u][p(str[i])]=newnode();
        u=ch[u][p(str[i])];
    }
    flag[u]=true;
}

void getfail(){
    queue<int> q;
    fail[root]=root;
    for(int i=0;i<4;++i)
    if(ch[root][i]){
        fail[ch[root][i]]=root;
        q.push(ch[root][i]);
    }

    int u=root;
    while(!q.empty()){
        u=q.front();q.pop();
        if(flag[fail[u]])
            flag[u]=true;
        for(int i=0;i<4;++i)
        if(ch[u][i]){
            fail[ch[u][i]]=ch[fail[u]][i];
            q.push(ch[u][i]);
        }
        else
            ch[u][i]=ch[fail[u]][i];
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值