【POJ】2778 DNA Sequence AC自动机+矩阵

DNA Sequence
Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 11143
Accepted: 4259

Description

It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For example, if a animal's DNA sequence contains segment ATC then it may mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don't contain those segments.

Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G,and the length of sequences is a given integer n.

Input

First line contains two integer m (0 <= m <= 10), n (1 <= n <=2000000000). Here, m is the number of genetic disease segment, and n is the length of sequences.

Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10.

Output

An integer, the number of DNA sequences, mod 100000.

Sample Input

4 3
AT
AC
AG
AA

Sample Output

36

Source

POJ Monthly--2006.03.26,dodo

传送门:【POJ】2778 DNA Sequence

题目大意:给你m (m<= 10) 个非法串,每个串长度不超过10,且只用A,C,G,T四个字符,问长度为n(n <= 2000000000)的串中完全不包含上述非法串的个数(即合法串)。结果模100000。

题目分析:
比较好的一题AC自动机+矩阵加速。
首先构造出转移,对于合法的一个状态为i的串,如果添加一个字符以后便包含了任意一个非法串则称这个状态不可达,反之则可达,我们的目的就是要根据合法的转移确立解。
那么合法的转移该怎么得到?没错,通过AC自动机的Trie图的构造,我们可以得到一个转移图。其中每一个合法的转移都意味着一条路径,而我们的目的就是找到长度为n的路。需要注意的是如果某个转移对应的状态是整个非法串或者K次转移后是非法串,则该次转移是非法的。还有就是mod的数是100000。。。正好相乘爆int

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( I , N ) for ( int I = 0 ; I < N ; ++ I )
#define CHARREP( I ) for ( int I = 0 ; buf[I] ; ++ I )
#define clear( A , X ) memset ( A , X , sizeof A )

const int maxW = 4 ;
const int maxM = 105 ;
const int mod = 100000 ;
const int maxQ = 1000000 ;

struct Matrix {
	int mat[maxM][maxM] ;
	int N ;
	Matrix () {}
	Matrix ( int n ) {
		N = n ;
		REP ( i , N ) REP ( j , N ) mat[i][j] = 0 ;
	}
	void Init () {
		REP ( i , N ) mat[i][i] = 1 ;
	}
	Matrix operator * ( const Matrix &m ) const {
		Matrix res = Matrix ( N ) ;
		REP ( i , N ) REP ( j , N ) REP ( k , N )
			res.mat[i][j] = ( res.mat[i][j] + ( long long ) mat[i][k] * m.mat[k][j] ) % mod ;
		return res ;
	}
} ;

struct Trie {
	int next[maxM][maxW] ;
	int fail[maxM] ;
	int end[maxM] ;
	int Q[maxQ] ;
	int P , root ;
	int head , tail ;
	int New () {
		REP ( i , maxW ) next[P][i] = -1 ;
		end[P] = 0 ;
		return P ++ ;
	}
	void Init () {
		P = 0 ;
		root = New () ;
	}
	int Get ( char x ) {
		if ( x == 'A' ) return 0 ;
		if ( x == 'C' ) return 1 ;
		if ( x == 'G' ) return 2 ;
		if ( x == 'T' ) return 3 ;
	}
	void Insert ( char buf[] ) {
		int now = root ; 
		CHARREP ( i ) {
			int x = Get ( buf[i] ) ;
			if ( next[now][x] == -1 ) next[now][x] = New () ;
			now = next[now][x] ;
		}
		end[now] = 1 ;
	}
	void Build () {
		head = tail = 0 ;
		fail[root] = root ;
		REP ( i , maxW ) {
			if ( next[root][i] == -1 ) next[root][i] = root ;
			else {
				fail[next[root][i]] = root ;
				Q[tail ++] = next[root][i] ;
			}
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			REP ( i , maxW ) {
				if ( next[now][i] == -1 ) next[now][i] = next[fail[now]][i] ;
				else {
					fail[next[now][i]] = next[fail[now]][i] ;
					end[next[now][i]] |= end[fail[next[now][i]]] ;
					Q[tail ++] = next[now][i] ;
				}
			}
		}
	}
	Matrix Matrix_Build () {
		Matrix res = Matrix ( P ) ;
		REP ( i , P ) REP ( j , maxW )
			if ( !end[next[i][j]] ) ++ res.mat[i][next[i][j]] ;
		return res ;
	}
} ;

Trie AC ;
char buf[maxM] ;

Matrix Pow ( Matrix a , int n ) {
	Matrix res = Matrix ( a.N ) ;
	Matrix tmp = a ;
	res.Init () ;
	while ( n ) {
		if ( n & 1 ) res = res * tmp ;
		tmp = tmp * tmp ;
		n >>= 1 ;
	}
	return res ;
}

void work () {
	int m , n ;
	while ( ~scanf ( "%d%d" , &m , &n ) ) {
		AC.Init () ;
		REP ( i , m ) {
			scanf ( "%s" , buf ) ;
			AC.Insert ( buf ) ;
		}
		AC.Build () ;
		Matrix res = AC.Matrix_Build () ;
		res = Pow ( res , n ) ;
		int ans = 0 ;
		REP ( i , res.N ) ans = ( ans + res.mat[0][i] ) % mod ;
		printf ( "%d\n" , ans ) ;
	}
}

int main () {
	work () ;
	return 0 ;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值