题意:与poj1625类似。有n种DNA序列(由AGCT四个字符组成)是有疾病的,问有多少种长度为m的DNA序列不包含任何一种有疾病的DNA序列。
思路:对疾病模式串建立AC自动机。由于m太大,不能用动归来做。建立自动机的邻接矩阵A(点只取非危险节点),值A[i][j]表示从节点i到节点j的路径数(即多少个字符能够使得从节点i到节点j,可知此矩阵的每行每列的和不会大于4)。那么A^n[i][j]即从节点i经过n个字符到达节点j的走法。 最终Σ(A[1,i]) mod 100000就是答案。
矩阵乘法用快速幂没的说,需要注意的是做乘法的时候100000的平方可能超过int,所以矩阵要用long long来存储。
#include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <queue> #include <cstdlib> using namespace std; #define INF 0x3fffffff #define clc(s,t) memset(s,t,sizeof(s)) #define N 105 #define M 100000 char s[15]; char hh[4] = {'A','G','T','C'}; int t[N][4],fail[N]; bool flag[N]; int top,n,m; struct matrix{ long long x[N][N]; }a,res; int f[N],len; void init(){ clc(t,-1); clc(fail,0); clc(flag,false); for(int i = 0;i<4;i++) t[0][i] = 1; top = 1; } int id(char x){ for(int i = 0;i<4;i++) if(hh[i] == x) return i; return -1; } void insert(char* s){ int i,j,r = 1; for(i = 0;s[i];i++){ j = id(s[i]); if(t[r][j] == -1) t[r][j] = (++top); r = t[r][j]; } flag[r] = true; } void buildDFA(){ int i,now; queue<int> q; q.push(1); while(!q.empty()){ now = q.front(); q.pop(); for(i = 0;i<4;i++){ if(t[now][i] == -1) t[now][i] = t[fail[now]][i]; else{ fail[t[now][i]] = t[fail[now]][i]; q.push(t[now][i]); if(flag[t[fail[now]][i]]) flag[t[now][i]] = true; } } } } struct matrix multi(struct matrix a,struct matrix b){ int i,j,k; struct matrix tmp; clc(tmp.x,0); for(i = 1;i<=len;i++) for(j = 1;j<=len;j++) for(k = 1;k<=len;k++){ tmp.x[i][j] += a.x[i][k]*b.x[k][j]; tmp.x[i][j] %= M; } return tmp; } int main(){ int i,j,sum=0; init(); scanf("%d %d",&n,&m); for(i = 1;i<=n;i++){ scanf("%s",s); insert(s); } buildDFA(); for(i =1,j=0;i<=top;i++){ if(!flag[i]) j++; f[i] = j; } len = j; clc(a.x,0); clc(res.x,0); for(i = 1;i<=top;i++){ if(flag[i]) continue; for(j = 0;j<4;j++) if(!flag[t[i][j]]) a.x[f[i]][f[t[i][j]]]++; } for(i = 1;i<=len;i++) res.x[i][i] = 1; while(m){ if(m&1) res = multi(res,a); m>>=1; a = multi(a,a); } for(i = 1;i<=len;i++) sum += res.x[1][i]; sum %= M; printf("%d\n",sum); return 0; }