欢迎访问~原文出处——博客园-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;
}