题意:与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;
}