POJ2778 DNA Sequence(AC自动机+矩阵快速幂)

时间:2022-05-29 09:34:08

题目给m个病毒串,问不包含病毒串的长度n的DNA片段有几个。

感觉这题好神,看了好久的题解。

所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个结点都可以看作是某个病毒串的前缀,Trie树的根则是空字符串。

而从根出发,在AC自动机上跑,经过k次转移到达某个结点,这个结点所代表的病毒串前缀可以看作长度为k的字符串的后缀,如果接下去跑往ATCG四个方向转移,就能到达新的结点,转移到新的长k+1字符串的后缀。

这样带着一个后缀状态的转移就能绕开病毒串,所以病毒串末尾的结点要标记,后缀存在病毒串的结点也要标记(这个在计算结点fail的时候就能处理),转移时就不能转移到被标记的结点。

接下来,题目的数据范围是10个长度10的病毒串,所以Trie树中最多101左右个结点,那么AC自动机整个转移就可以构建一张101*101的邻接矩阵,矩阵i行j列的权值是结点i转移到结点j的方案数。

而进行k次转移,从结点i转移到结点j的方案数是这个矩阵的k次幂,这个结论离散数学的图论有。。

所以,长度n的字符串的方案数,就是转移n次根结点能到所有结点的方案和就是答案。就是计算矩阵的n次幂,统计根所在行的数字和,n的达到20亿用矩阵快速幂即可。

(POJ从昨天就挂了。。SCU有原题,多组数据,http://acm.scu.edu.cn/soj/problem.action?id=3030

 #include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int ch[][],fail[],tn;
bool flag[]; int idx[];
void insert(char *s){
int x=;
for(int i=; s[i]; ++i){
int y=idx[s[i]];
if(ch[x][y]==) ch[x][y]=++tn;
x=ch[x][y];
}
flag[x]=;
}
void init(){
memset(fail,,sizeof(fail));
queue<int> que;
for(int i=; i<; ++i){
if(ch[][i]) que.push(ch[][i]);
}
while(!que.empty()){
int now=que.front(); que.pop();
for(int i=; i<; ++i){
if(ch[now][i]) que.push(ch[now][i]),fail[ch[now][i]]=ch[fail[now]][i];
else ch[now][i]=ch[fail[now]][i];
flag[ch[now][i]]|=flag[ch[fail[now]][i]];
}
}
}
struct Mat{
long long mat[][];
Mat(){
memset(mat,,sizeof(mat));
}
};
Mat operator*(const Mat &m1,const Mat &m2){
Mat m;
for(int i=; i<=tn; ++i){
for(int j=; j<=tn; ++j){
for(int k=; k<=tn; ++k){
m.mat[i][j]+=m1.mat[i][k]*m2.mat[k][j];
m.mat[i][j]%=;
}
}
}
return m;
}
int main(){
idx['A']=; idx['C']=; idx['T']=; idx['G']=;
char str[];
int m,n;
while(~scanf("%d%d",&m,&n)){
tn=;
memset(flag,,sizeof(flag));
memset(ch,,sizeof(ch));
while(m--){
scanf("%s",str);
insert(str);
}
init();
Mat e,x;
for(int i=; i<=tn; ++i) e.mat[i][i]=;
for(int i=; i<=tn; ++i){
if(flag[i]) continue;
for(int j=; j<; ++j){
if(flag[ch[i][j]]) continue;
++x.mat[i][ch[i][j]];
}
}
while(n){
if(n&) e=e*x;
x=x*x;
n>>=;
}
long long res=;
for(int i=; i<=tn; ++i){
res+=e.mat[][i];
res%=;
}
printf("%lld\n",res);
}
return ;
}