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;
}

  

 

posted @ 2017-11-02 22:02  zzd233  阅读(315)  评论(0编辑  收藏  举报