zoukankan      html  css  js  c++  java
  • POJ2778 DNA Sequence(AC自动机+矩阵快速幂)

    题目给m个病毒串,问不包含病毒串的长度n的DNA片段有几个。

    感觉这题好神,看了好久的题解。

    所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个结点都可以看作是某个病毒串的前缀,Trie树的根则是空字符串。

    而从根出发,在AC自动机上跑,经过k次转移到达某个结点,这个结点所代表的病毒串前缀可以看作长度为k的字符串的后缀,如果接下去跑往ATCG四个方向转移,就能到达新的结点,转移到新的长k+1字符串的后缀。

    这样带着一个后缀状态的转移就能绕开病毒串,所以病毒串末尾的结点要标记,后缀存在病毒串的结点也要标记(这个在计算结点fail的时候就能处理),转移时就不能转移到被标记的结点。

    接下来,题目的数据范围是10个长度10的病毒串,所以Trie树中最多101左右个结点,那么AC自动机整个转移就可以构建一张101*101的邻接矩阵,矩阵i行j列的权值是结点i转移到结点j的方案数。

    而进行k次转移,从结点i转移到结点j的方案数是这个矩阵的k次幂,这个结论离散数学的图论有。。

    所以,长度n的字符串的方案数,就是转移n次根结点能到所有结点的方案和就是答案。就是计算矩阵的n次幂,统计根所在行的数字和,n的达到20亿用矩阵快速幂即可。

    (POJ从昨天就挂了。。SCU有原题,多组数据,http://acm.scu.edu.cn/soj/problem.action?id=3030

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 using namespace std;
     5 int ch[111][4],fail[111],tn;
     6 bool flag[111];
     7 
     8 int idx[128];
     9 void insert(char *s){
    10     int x=0;
    11     for(int i=0; s[i]; ++i){
    12         int y=idx[s[i]];
    13         if(ch[x][y]==0) ch[x][y]=++tn;
    14         x=ch[x][y];
    15     }
    16     flag[x]=1;
    17 }
    18 void init(){
    19     memset(fail,0,sizeof(fail));
    20     queue<int> que;
    21     for(int i=0; i<4; ++i){
    22         if(ch[0][i]) que.push(ch[0][i]);
    23     }
    24     while(!que.empty()){
    25         int now=que.front(); que.pop();
    26         for(int i=0; i<4; ++i){
    27             if(ch[now][i]) que.push(ch[now][i]),fail[ch[now][i]]=ch[fail[now]][i];
    28             else ch[now][i]=ch[fail[now]][i];
    29             flag[ch[now][i]]|=flag[ch[fail[now]][i]];
    30         }
    31     }
    32 }
    33 struct Mat{
    34     long long mat[111][111];
    35     Mat(){
    36         memset(mat,0,sizeof(mat));
    37     }
    38 };
    39 Mat operator*(const Mat &m1,const Mat &m2){
    40     Mat m;
    41     for(int i=0; i<=tn; ++i){
    42         for(int j=0; j<=tn; ++j){
    43             for(int k=0; k<=tn; ++k){
    44                 m.mat[i][j]+=m1.mat[i][k]*m2.mat[k][j];
    45                 m.mat[i][j]%=100000;
    46             }
    47         }
    48     }
    49     return m;
    50 }
    51 int main(){
    52     idx['A']=0; idx['C']=1; idx['T']=2; idx['G']=3;
    53     char str[11];
    54     int m,n;
    55     while(~scanf("%d%d",&m,&n)){
    56         tn=0;
    57         memset(flag,0,sizeof(flag));
    58         memset(ch,0,sizeof(ch));
    59         while(m--){
    60             scanf("%s",str);
    61             insert(str);
    62         }
    63         init();
    64         Mat e,x;
    65         for(int i=0; i<=tn; ++i) e.mat[i][i]=1;
    66         for(int i=0; i<=tn; ++i){
    67             if(flag[i]) continue;
    68             for(int j=0; j<4; ++j){
    69                 if(flag[ch[i][j]]) continue;
    70                 ++x.mat[i][ch[i][j]];
    71             }
    72         }
    73         while(n){
    74             if(n&1) e=e*x;
    75             x=x*x;
    76             n>>=1;
    77         }
    78         long long res=0;
    79         for(int i=0; i<=tn; ++i){
    80             res+=e.mat[0][i];
    81             res%=100000;
    82         }
    83         printf("%lld
    ",res);    
    84     }
    85     return 0;
    86 }
  • 相关阅读:
    结对编程——四则运算
    需求分析
    结对编程
    调查问卷的心得体会
    软件工程课初步目标
    软件工程课程建议
    结对编程--fault,error,failure
    结对编程--四则运算
    需求分析
    结对编程
  • 原文地址:https://www.cnblogs.com/WABoss/p/5167101.html
Copyright © 2011-2022 走看看