| Time Limit: 1000MS | Memory Limit: 65536K | |
| Total Submissions: 15118 | Accepted: 5826 |
Description
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
Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10.
Output
Sample Input
4 3 AT AC AG AA
Sample Output
36
Source
题目意思:
有M个含有疾病的DNA序列,求出用AGCT四种字符构成长度为N的DNA序列,使之不含有疾病序列的总数。
解题思路:
把ac自动机看成一个有向图,构建一个邻接矩阵,那么matrix[i][j]表示i和j是否可达,这个矩阵的n次幂matrix^n[i][j]表示从i恰好走n步到达j的路径有几条。
下面搬运一下:
2 1 0 0 1
2 1 1 0 0
1 1 0 1 1
2 1 0 0 1
2 1 0 0 1
M[i,j]表示从结点i到j只走一步有几种走法。
那么M的n次幂就表示从结点i到j走n步有几种走法。
③去掉危险结点,也就是去掉危险结点的行和列。结点3和4是单词结尾所以危险,结点2的fail指针指向4,当匹配”AC”时也就匹配了”C”,所以2也是危险的。
矩阵变成M:
2 1
2 1
④计算M[][]的n次幂,然后 Σ(M[0,i]) mod 100000 就是答案。
由于n很大,可以使用二分来计算矩阵的幂
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
const int MOD=100000;
struct Matrix
{
int mat[110][110],n;
Matrix() {}
Matrix(int _n)
{
n = _n;
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
mat[i][j]=0;
}
Matrix operator *(const Matrix &b)const
{
Matrix ret=Matrix(n);
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
for(int k=0; k<n; k++)
{
int tmp=(long long)mat[i][k]*b.mat[k][j]%MOD;
ret.mat[i][j]=(ret.mat[i][j]+tmp)%MOD;
}
return ret;
}
};
struct Trie
{
int next[110][4],fail[110];
bool end[110];
int root,L;
int newnode()
{
for(int i=0; i<4; i++)
next[L][i]=-1;
end[L++]=false;
return L-1;
}
void init()
{
L=0;
root=newnode();
}
int getch(char ch)
{
switch(ch)
{
case 'A':
return 0;
break;
case 'C':
return 1;
break;
case 'G':
return 2;
break;
case 'T':
return 3;
break;
}
}
void insert(char s[])
{
int len=strlen(s);
int now=root;
for(int i = 0; i < len; i++)
{
if(next[now][getch(s[i])] == -1)
next[now][getch(s[i])] = newnode();
now = next[now][getch(s[i])];
}
end[now]=true;
}
void build()
{
queue<int>Q;
for(int i = 0; i < 4; i++)
if(next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while(!Q.empty())
{
int now = Q.front();
Q.pop();
if(end[fail[now]]==true)
end[now]=true;
for(int i = 0; i < 4; 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]);
}
}
}
}
Matrix getMatrix()
{
Matrix res = Matrix(L);
for(int i=0; i<L; i++)
for(int j=0; j<4; j++)
if(end[next[i][j]]==false)
res.mat[i][next[i][j]]++;
return res;
}
};
Trie ac;
char buf[20];
Matrix pow_M(Matrix a,int n)
{
Matrix ret = Matrix(a.n);
for(int i = 0; i < ret.n; i++)
ret.mat[i][i]=1;
Matrix tmp=a;
while(n)
{
if(n&1)ret=ret*tmp;
tmp=tmp*tmp;
n>>=1;
}
return ret;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) != EOF)
{
ac.init();
for(int i=0; i<n; i++)
{
scanf("%s",buf);
ac.insert(buf);
}
ac.build();//插入字符串构建AC自动机,根据trie图构建邻接矩阵
Matrix a=ac.getMatrix();//从矩阵中去掉含疾病的危险节点所在行列
a=pow_M(a,m);
int ans=0;
for(int i=0; i<a.n; i++)
{
ans=(ans+a.mat[0][i])%MOD;
}
printf("%d\n",ans);
}
return 0;
}

本文介绍了一种使用AC自动机和矩阵快速幂的方法来解决DNA序列计数问题,即求出长度为N的DNA序列中不含特定病态子序列的数量。
858

被折叠的 条评论
为什么被折叠?



