Poj 2778 DNA Sequence (字符串_AC自动机(DP))

题目链接:http://poj.org/problem?id=2778


题目大意:给定‘A''C''G''T'四个碱基组成的m个病毒字符串,求碱基组成的长度为n的不包含病毒串的字符串种数,结果模以10万。


解题思路:状态转移方程很容易想:if (j->next非危险节点) dp[i+1][j->next] += dp[i][j];(i表示长度,j->next为自动机上j位置下一个到达的位置)。但这题的n也就是长度异常地大,用普通的dp做太不靠谱了。刚学完矩阵乘法(开始时觉得很难,但看了一晚上发现就会做了),想着能否用矩阵来求解。由于上述方程并非线性的,就算转为线性也非常麻烦,再想着构造其他模型。

      其实这题里的AC自动机也是一张有向图,i->j有边当且仅当i-》next == j.所以转移也就是图上的某个点走到另一个点,那遇到病毒串是什么情况?肯定没办法到病毒串末尾的危险节点,所以没有边连到危险节点。这样的话模型转换为求有向图上0节点到其他能到达的节点的路径,并且经过n条边。离散数学里面有说把这个矩阵A进行A^n计算然后累加就ok。

     本题的取模数为10万,在乘法运算中容易超32位int,所以矩阵中的数组应为64位整数。


测试数据:

0 3


4 3
AT
AC
AG

AA


4 1000000000
AT
AC
AG
AA



代码:

#include <stdio.h>
#include <string.h>
#define MAX 110
#define MOD 100000


char dir[MAX];
int  map[MAX][MAX];
int  n,m,total,hash[256];

struct node {

	int flag,in;
	node *fail,*next[4];
}*qu[MAX*5],arr[MAX],*root;
struct Mat {

	__int64 mat[MAX][MAX],size;
	Mat(int n) {size = n,memset(mat,0,sizeof(mat));}


	friend Mat operator *(Mat a,Mat b);
	friend Mat operator +(Mat a,Mat b);
	friend Mat operator ^(Mat a,Mat b);
}E(MAX),A(MAX);



Mat operator *(Mat a,Mat b) {

	Mat c(total);
	for (int i = 0; i < c.size; ++i)
		for (int j = 0; j < c.size; ++j)
			for (int k = 0; k < c.size; ++k)
				if (a.mat[i][k] && b.mat[k][j])
					c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
	return c;
}
Mat operator +(Mat a,Mat b) {

	Mat c(total);
	for (int i = 0; i < c.size; ++i)
		for (int j = 0; j < c.size; ++j)
			c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % MOD;
	return c;
}
Mat operator ^(Mat a,int k) {

	Mat c = E;
	while (k) {

		if (k & 1) c = c * a;
		a = a * a,k >>= 1;
	}
	return c;
}


node *CreateNode() {

	node *p = &arr[total];
	p->in = total++;
	p->flag = 0;
	p->fail = NULL;
	for (int i = 0; i < 4; ++i)
		p->next[i] = NULL;
	return p;
}
void Insert(char *str) {

	int i = 0,k;
	node *p = root;


	while (str[i]) {

		k = hash[str[i++]]; 
		if (p->next[k] == NULL)
			p->next[k] = CreateNode();
		p = p->next[k];
	}
	p->flag = 1;
}
void Build_AC() {

	int i,k,head,tail;
	head = tail = 0;


	root->fail = root;
	qu[++head] = root;
	while (tail < head) {

		node *p = qu[++tail];
		for (k = 0; k < 4; ++k) 
			if (p->next[k] != NULL) {

				if (p == root) p->next[k]->fail = root;
				else p->next[k]->fail = p->fail->next[k];
				if (p->fail->next[k]->flag == 1)
					p->next[k]->flag = 1;
				qu[++head] = p->next[k];
			}
			else {

				if (p == root) p->next[k] = root;
				else p->next[k] = p->fail->next[k];
			}
	}
}
void Create_Mat() {

	int i,j,k;
	A.size = E.size = total;

	for (i = 0; i < E.size; ++i)
		E.mat[i][i] = 1;
	for (i = 0; i < total; ++i)
		if (arr[i].flag == 0) {

			for (k = 0; k < 4; ++k)
				if (arr[i].next[k]->flag == 0)
					A.mat[i][arr[i].next[k]->in]++;
		}
}


int main()
{
	int i,j,k,ans;
	hash['A'] = 0,hash['C'] = 1;
	hash['G'] = 2,hash['T'] = 3;


	while (scanf("%d%d",&m,&n)!= EOF) {

		total = ans = 0;
		root = CreateNode();
		memset(A.mat,0,sizeof(A.mat));
		for (i = 0; i < m; ++i)
			scanf("%s",dir),Insert(dir);


		Build_AC();
		Create_Mat();
		A = A^n;
		for (i = 0; i < total; ++i)
			if (arr[i].flag == 0) 
				ans = (ans + A.mat[0][arr[i].in]) % MOD;
		printf("%d\n",ans);
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值