POJ2778 DNA Sequence(AC自动机+矩阵快速幂)
题目给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)
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 using namespace std; 5 int ch[111][4],fail[111],tn; 6 bool flag[111]; 7 8 int idx[128]; 9 void insert(char *s){ 10 int x=0; 11 for(int i=0; s[i]; ++i){ 12 int y=idx[s[i]]; 13 if(ch[x][y]==0) ch[x][y]=++tn; 14 x=ch[x][y]; 15 } 16 flag[x]=1; 17 } 18 void init(){ 19 memset(fail,0,sizeof(fail)); 20 queue<int> que; 21 for(int i=0; i<4; ++i){ 22 if(ch[0][i]) que.push(ch[0][i]); 23 } 24 while(!que.empty()){ 25 int now=que.front(); que.pop(); 26 for(int i=0; i<4; ++i){ 27 if(ch[now][i]) que.push(ch[now][i]),fail[ch[now][i]]=ch[fail[now]][i]; 28 else ch[now][i]=ch[fail[now]][i]; 29 flag[ch[now][i]]|=flag[ch[fail[now]][i]]; 30 } 31 } 32 } 33 struct Mat{ 34 long long mat[111][111]; 35 Mat(){ 36 memset(mat,0,sizeof(mat)); 37 } 38 }; 39 Mat operator*(const Mat &m1,const Mat &m2){ 40 Mat m; 41 for(int i=0; i<=tn; ++i){ 42 for(int j=0; j<=tn; ++j){ 43 for(int k=0; k<=tn; ++k){ 44 m.mat[i][j]+=m1.mat[i][k]*m2.mat[k][j]; 45 m.mat[i][j]%=100000; 46 } 47 } 48 } 49 return m; 50 } 51 int main(){ 52 idx['A']=0; idx['C']=1; idx['T']=2; idx['G']=3; 53 char str[11]; 54 int m,n; 55 while(~scanf("%d%d",&m,&n)){ 56 tn=0; 57 memset(flag,0,sizeof(flag)); 58 memset(ch,0,sizeof(ch)); 59 while(m--){ 60 scanf("%s",str); 61 insert(str); 62 } 63 init(); 64 Mat e,x; 65 for(int i=0; i<=tn; ++i) e.mat[i][i]=1; 66 for(int i=0; i<=tn; ++i){ 67 if(flag[i]) continue; 68 for(int j=0; j<4; ++j){ 69 if(flag[ch[i][j]]) continue; 70 ++x.mat[i][ch[i][j]]; 71 } 72 } 73 while(n){ 74 if(n&1) e=e*x; 75 x=x*x; 76 n>>=1; 77 } 78 long long res=0; 79 for(int i=0; i<=tn; ++i){ 80 res+=e.mat[0][i]; 81 res%=100000; 82 } 83 printf("%lld\n",res); 84 } 85 return 0; 86 }