POJ2778 DNA Sequence AC自动机 矩阵
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - POJ2778
题意概括
现在有一个长度为n(n<=2000000000)的DNA串,其中只可能有A、C、G、T四种字母。现在给出m(m<=10)个危险串(len<=10),求有几种可行的安全串。最终的答案mod 100000。
题解
我们先按照输入的危险串构建AC自动机。
对于当前串在AC自动机上的某一个状态k,我们接下来填一个字母,会有4种不同的转移。
会转移到其它的不同状态。
但是不管是从病毒串出发还是到达,都不能涉及,所以,与病毒串相关的转移都要舍去。
于是我们发下对于每一个状态,每次添加一个字母,所影响的下一个状态是一定的。
或者说对于某一个状态的总答案,一定是一个固定的由之前递推而来的递推式。
具体可以参考代码。
然后我们发现可以用矩阵快速幂优化。
最后的答案就是从AC自动机虚点转移到其他所有节点(包括虚点)的方案总数。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int MatSize=105,L=15,mod=100000; int n,m,s,cnt,Turn[150]; char ch[L]; struct Trie{ int e,fail,Next[4]; void init(){ e=fail=0; memset(Next,0,sizeof Next); } }tree[MatSize]; void AC_init(){ cnt=1; tree[0].init(); tree[1].init(); for (int i=0;i<4;i++) tree[0].Next[i]=1; } void build(char ch[]){ int rt=1,t,len=strlen(ch); for (int i=0;i<len;i++){ t=Turn[ch[i]]; if (!tree[rt].Next[t]) tree[tree[rt].Next[t]=++cnt].init(); rt=tree[rt].Next[t]; } tree[rt].e=1; } int q[MatSize],head,tail; void build_AC(){ int rt,k,son; head=tail=0; tree[0].fail=1,tree[1].fail=0; q[++tail]=1; while (head<tail){ rt=q[++head]; for (int i=0;i<4;i++){ son=tree[rt].Next[i]; if (!son){ tree[rt].Next[i]=tree[tree[rt].fail].Next[i]; continue; } k=tree[rt].fail; while (!tree[k].Next[i]) k=tree[k].fail; tree[son].fail=tree[k].Next[i]; tree[son].e|=tree[tree[k].Next[i]].e; q[++tail]=son; } } } struct Mat{ LL v[MatSize][MatSize]; Mat (){} Mat (int x){ (*this).set(x); } void set(int x){ memset(v,0,sizeof v); if (x==0) return; for (int i=1;i<=s;i++) v[i][i]=1; } Mat operator * (Mat a){ Mat ans(0); for (int i=1;i<=s;i++) for (int j=1;j<=s;j++) for (int k=1;k<=s;k++) ans.v[i][j]=(ans.v[i][j]+v[i][k]*a.v[k][j])%mod; return ans; } void operator *= (Mat a){ (*this)=(*this)*a; } }M,Mans; Mat MatPow(Mat x,int y){ Mat ans(1),now=x; while (y){ if (y&1) ans*=now; now*=now; y>>=1; } return ans; } int main(){ memset(Turn,-1,sizeof -1); Turn['A']=0,Turn['C']=1,Turn['G']=2,Turn['T']=3; scanf("%d%d",&m,&n); AC_init(); for (int i=1;i<=m;i++){ scanf("%s",ch); build(ch); } build_AC(); s=cnt; M.set(0); for (int i=1;i<=s;i++) for (int j=0;j<4;j++){ int rt=i,son=tree[i].Next[j]; if (!tree[rt].e&&!tree[son].e) M.v[rt][son]++; } Mans=MatPow(M,n); LL ans=0; for (int i=1;i<=s;i++) ans=(ans+Mans.v[1][i])%mod; printf("%lld",ans); return 0; }