题意:
给出n个AGCT组成的字符串和一个数m;
求AGCT能组成的长度为m的串的个数;
n<=10,字符串长度<=10,m<=2x10^9;
题解:
构造一个满足题意的长度为x的串之前,我们要先构筑出一个长度为x-1的串;
显然倘若要让x的符合题意,x-1的必符合题意;
那么既然串的前半部分都已经符合题意了,我们只需要考虑x-1那个串的后缀;
就是说那个后缀再加上一个字符之后,能否满足题意;
而当构造x+1的串时,x的后缀成为新的要考虑的状态;
那么就可以说,x-1的后缀状态是与x的状态有连边的,x与x+1的也是;
那么我们倘若构造出了对于所有的x之间的连边关系的图,以其为邻接矩阵做矩阵乘法就可以了;
实际上,因为能否成功构成下一个串只与后缀有关,而阻止构成的字符串只有10个,长度为10;
所以需要讨论的状态最多有100种;
那么处理矩阵就用ac自动机,字典树上的每个点都可以代表一个后缀;
在后缀后面加上字符,可能将他向下拓展,或者因为匹配不能而跳转到fail指针下的next[i]处;
然后有病毒的地方是不能连边的,有病毒作为子串的后缀也是如此;
时间复杂度大概是O(n^2+100^3lgm)的样子;
数组版的AC自动机不太习惯,写的较丑勿怪;
代码:
#include<queue> #include<stdio.h> #include<string.h> #include<algorithm> #define N 101 #define mod 100000 using namespace std; typedef long long ll; struct trie { int fail,next[4]; bool is; }tr[N]; struct matrix { ll a[N][N]; }In,T,ans; queue<int>q; char str[20]; int tot; int in(char ch) { switch(ch) { case'A':return 0; case'G':return 1; case'C':return 2; case'T':return 3; }; } matrix mul(matrix x,matrix y) { matrix ret; int i,j,k; for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) for(k=1,ret.a[i][j]=0;k<=tot;k++) ret.a[i][j]=(ret.a[i][j]+x.a[i][k]*y.a[k][j])%mod; return ret; } matrix pow(matrix x,ll y) { matrix ret=In; while(y) { if(y&1) ret=mul(ret,x); x=mul(x,x); y>>=1; } return ret; } void insert(int root,char *s) { int index,p=root; while(*s!='\0') { index=in(*s); if(tr[p].next[index]==0) tr[p].next[index]=++tot; p=tr[p].next[index]; s++; } tr[p].is=1; } void build(int root) { q.push(root); while(!q.empty()) { int p=q.front(); q.pop(); for(int i=0;i<4;i++) { if(tr[p].next[i]) { if(p==root) tr[tr[p].next[i]].fail=root; else { int temp=tr[p].fail; while(temp) { if(tr[temp].next[i]) { tr[tr[p].next[i]].fail=tr[temp].next[i]; tr[tr[p].next[i]].is|=tr[tr[temp].next[i]].is; break; } temp=tr[temp].fail; } if(temp==0) tr[tr[p].next[i]].fail=root; } q.push(tr[p].next[i]); } else { if(p==root) tr[p].next[i]=root; else tr[p].next[i]=tr[tr[p].fail].next[i]; } } } } void initmat(int root) { int i,j,k; for(i=1;i<=tot;i++) { if(tr[i].is) continue; for(k=0;k<4;k++) { if(tr[j=tr[i].next[k]].is) continue; T.a[i][j]++; } } for(i=1;i<=tot;i++) In.a[i][i]=1; } int main() { int n,i,j,k; ll m,sum; scanf("%d%lld",&n,&m); for(i=1,tot=1;i<=n;i++) { scanf("%s",str); insert(1,str); } build(1); initmat(1); ans=pow(T,m); for(i=1,sum=0;i<=tot;i++) sum=(sum+ans.a[1][i])%mod; printf("%lld",sum); return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/45897855