POJ2778ac自动机+矩阵加速

求长度为M的DNA串不包含模式串的有多少个。

    step1::先考虑另外一个问题,给你一个有向图,从某个点出发走n步到其他任意点,问不同的走法。

  对这个问题构造一个矩阵,matrix[i][j]表示从i到j有几条边,然后这个矩阵自乘n次就是答案。

     step2 :       点击打开链接

                        点击打开链接

    


2张图片均转自上述衔接博客,感谢。


typedef long long LL ;

const   LL   mod = 100000 ;
int     matn ;
struct  Mat{
        LL x[108][108] ;
        Mat(){
             memset(x , 0 , sizeof(x)) ;
        }
        Mat(int){
             memset(x , 0 , sizeof(x)) ;
             for(int i = 0 ; i < matn ; i++)
                 x[i][i] = 1 ;
        }
};

Mat operator * (Mat a , Mat b){
        Mat s ;
        int n = matn  , i , j , k ;
        for(i = 0 ; i < n ; i++){
            for(j = 0 ; j < n ; j++){
                if(a.x[i][j] == 0) continue ;
                for(k = 0 ; k < n ; k++){
                     s.x[i][k] += a.x[i][j] * b.x[j][k] ;
                     s.x[i][k] %= mod ;
                }
            }
        }
        return s ;
}

Mat  Pow(Mat x , int y){
     Mat s(1) ;
     for(; y ; y >>= 1){
          if(y & 1) s = s * x ;
          x = x * x ;
     }
     return s ;
}

/*AC-------------*/
const  int maxn = 108*4 ;
const  int kind = 4 ;
int    next[maxn][kind] ;
int    fail[maxn] ;
int    id[maxn]   ;

struct  AC{
        int  root , n ;
        int  newnode(){
             id[n] = 0 ;
             for(int i = 0 ; i < kind ; i++) next[n][i] = -1 ;
             return n++ ;
        }
        void  Init(){
              n = 0 ;
              root = newnode() ;
        }
        int   to(char c){
              if(c == 'A') return 0 ;
              else if(c == 'C') return 1 ;
              else if(c == 'T') return 2 ;
              else return 3 ;
        }
        void  Insert(char *s){
              int now = root  , k ;
              while(*s){
                   k = to(*s) ;
                   if(next[now][k] == -1)
                       next[now][k] = newnode() ;
                   now = next[now][k] ;
                   s++ ;
              }
              id[now] = 1  ;
        }
        void  makeAc(){
              queue<int> q ;
              fail[root] = root ;
              int now , i ;
              for(i = 0 ; i < kind ; i++){
                   if(next[root][i] == -1)
                       next[root][i] = root ;
                   else{
                       fail[next[root][i]] =  root ;
                       q.push(next[root][i]) ;
                   }
              }
              while(! q.empty()){
                   now = q.front() ; q.pop() ;
                   if(id[fail[now]] == 1)  id[now] = 1 ;
                   for(i = 0 ; i < kind ; i++){
                       if(next[now][i] == -1)
                           next[now][i] = next[fail[now]][i] ;
                       else{
                           fail[next[now][i]] = next[fail[now]][i] ;
                           q.push(next[now][i]) ;
                       }
                   }
              }
        }
        Mat  getMat(){
             matn = n ;
             Mat s ;
             for(int i = 0 ; i < n ; i++){
                 if(id[i]) continue ;
                 for(int j = 0 ; j < kind ; j++){
                      if(id[next[i][j]] == 0){
                           s.x[i][next[i][j]]++ ;
                      }
                 }
             }
             return s ;
        }
}ac ;
/*EndAc---------------*/

char  word[18] ;

int   main(){
      int t , n  , i  , m  ,j ;
      while(cin>>n>>m){
           ac.Init() ;
           for(i = 1 ; i <= n ; i++){
                scanf("%s" , word) ;
                ac.Insert(word) ;
           }
           ac.makeAc() ;
           Mat a = ac.getMat() ;
           a = Pow(a , m) ;
           LL sum = 0 ;
           for(i = 0 ; i < matn ; i++){
               sum += a.x[0][i] ;
               sum %= mod ;
           }
           cout<<sum<<endl ;
      }
      return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值