您的位置:首页 > 产品设计 > UI/UE

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

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

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

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

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

        因此最后的答案就是∑dp
[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];
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息