POJ 2778

主要利用状态之间的转换吧,可以把各个状态之间的转换成矩阵,利用矩阵乘法来找出有多少条路径。题解转自:

http://blog.csdn.net/morgan_xww/article/details/7834801

•题意:有m种DNA序列是有疾病的,问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列。(仅含A,T,C,G四个字符)

•样例m=4,n=3,{“AA”,”AT”,”AC”,”AG”}

•答案为36,表示有36种长度为3的序列可以不包含疾病

这个和矩阵有什么关系呢???

•上图是例子{“ACG”,”C”},构建trie图后如图所示,从每个结点出发都有4条边(A,T,C,G)

•从状态0出发走一步有4种走法:

–走A到状态1(安全);

–走C到状态4(危险);

–走T到状态0(安全);

–走G到状态0(安全);

•所以当n=1时,答案就是3

•当n=2时,就是从状态0出发走2步,就形成一个长度为2的字符串,只要路径上没有经过危险结点,有几种走法,那么答案就是几种。依此类推走n步就形成长度为n的字符串。

•建立trie图的邻接矩阵M:

2 1 0 0 1

2 1 1 0 0

1 1 0 1 1

2 1 0 0 1

2 1 0 0 1

M[i,j]表示从结点i到j只走一步有几种走法。

那么M的n次幂就表示从结点i到j走n步有几种走法。

注意:危险结点要去掉,也就是去掉危险结点的行和列。结点3和4是单词结尾所以危险,结点2的fail指针指向4,当匹配”AC”时也就匹配了”C”,所以2也是危险的。

矩阵变成M:

2 1

2 1

计算M[][]的n次幂,然后 Σ(M[0,i]) mod 100000 就是答案。

由于n很大,可以使用二分来计算矩阵的幂

自己写的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <string.h>
#include <queue>
#include <cmath>
#include <vector>
#define LL __int64
using namespace std;
const int dictsize=4;
const int QN=1000;
const int root=0;
const LL MOD=100000;
int ID[130];
int head,tail;
int que[QN];
struct Node {
	int fail,next[dictsize];
	bool tag;
	void initial(){
		fail=-1,tag=false;
		for(int i=0;i<dictsize;i++) next[i]=-1;
	}
}trie[150];
int tot,n,l;
char str[15];

struct Matrix{
	LL mat[105][105];
};
Matrix a,per;

Matrix operator *(Matrix a,Matrix b){
	Matrix c;
	for(int i=0;i<=tot;i++){
		for(int j=0;j<=tot;j++){
			c.mat[i][j]=0;
			for(int k=0;k<=tot;k++)
			c.mat[i][j]=(c.mat[i][j]+a.mat[i][k]*b.mat[k][j])%MOD;
		}
	}
	return c;
}

void Insert_trie(){
	int p=0,i=0;
	while(str[i]){
		if(trie[p].next[ID[str[i]]]==-1) trie[p].next[ID[str[i]]]=++tot;
		p=trie[p].next[ID[str[i]]];
		i++;
	}
	trie[p].tag=true;
}

void build_ac(){
	que[tail++]=root;
	int i,tmp,p;
	while(head!=tail){
		tmp=que[head++];
		p=-1;
		for(int i=0;i<dictsize;i++){
			if(trie[tmp].next[i]!=-1){
				if(tmp==root) trie[trie[tmp].next[i]].fail=root;
				else{
					p=trie[tmp].fail;
					while(p!=-1){
						if(trie[p].next[i]!=-1){
							trie[trie[tmp].next[i]].fail=trie[p].next[i];
							break;
						}
						p=trie[p].fail;
					}
					if(p==-1){
						trie[trie[tmp].next[i]].fail=root;
					}
				}
				if(trie[trie[trie[tmp].next[i]].fail].tag){
					trie[trie[tmp].next[i]].tag=true;
				}
				que[tail++]=trie[tmp].next[i];
			}
			else{	//trie[tmp].next[i]==-1
				if(tmp==root) trie[tmp].next[i]=root;
				else{
					p=trie[tmp].fail;
					while(p!=-1){
						if(trie[p].next[i]!=-1){
							trie[tmp].next[i]=trie[p].next[i];
							break;
						}
						p=trie[p].fail;
					}
					if(p==-1){
						trie[tmp].next[i]=root;
					}
				}
			}
		}
	}
}

void build_map(){
	memset(a.mat,0,sizeof(a.mat));
	for(int i=0;i<=tot;i++){
		for(int j=0;j<dictsize;j++){
			if(!trie[i].tag&&!trie[trie[i].next[j]].tag)
			a.mat[i][trie[i].next[j]]++;
		}
	}
}

Matrix multi(int k){
	Matrix ans,p=a;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=0;i<=tot;i++)
	ans.mat[i][i]=1;
	while(k){
		if(k&1) ans=ans*p;
		k>>=1;
		p=p*p;
	}
	return ans;
}

int main(){

	ID[‘A‘]=0,ID[‘G‘]=1,ID[‘C‘]=2,ID[‘T‘]=3;
	while(scanf("%d%d",&n,&l)!=EOF){
		tot=head=tail=0;
		for(int i=0;i<110;i++)
		trie[i].initial();
		for(int i=0;i<n;i++){
			scanf("%s",str);
			Insert_trie();
		}
		build_ac();
		build_map();
		Matrix ans=multi(l);
		LL print=0;
		for(int i=0;i<=tot;i++)
		print=(print+ans.mat[0][i])%MOD;
		printf("%I64d\n",print);
	}
	return 0;
}

  

时间: 2024-09-20 19:30:08

POJ 2778的相关文章

poj 2778 AC自动机 + 矩阵快速幂

// poj 2778 AC自动机 + 矩阵快速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就可以得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自动机有一些特别,根节点是为空串,然而 // 每走一步的时候,如果没法走了,这时候,不一定是回到根 // 节点,因为有可能单个的字符时病毒,这样

poj 2778 AC自动机与矩阵连乘

http://poj.org/problem?id=2778 Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For example, if a animal's DNA sequence contains segment ATC then it may

poj 2778 DNA Sequence(AC自动机+矩阵快速幂)

题目链接:poj 2778 DNA Sequence 题目大意:给定一些含有疾病的DNA序列,现在给定DNA长度,问有多少种不同的DNA序列是健康的. 解题思路:对DNA片段建立AC自动机,因为最多10个串,每个串最长为10,所以最多可能有100个节点,在长度为n时 以每个节点终止的健康字符串个数形成一个状态集,通过AC自动机形成的边可以推导出n+1的状态集,走到单词节点是 非法的,所以同样的我们可以先走到单词节点,但是从单词节点不向后转移.这样可以构造一个矩阵,剩下的就是矩阵 快速幂.注意的一

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就

poj 2778 AC自己主动机 + 矩阵高速幂

// poj 2778 AC自己主动机 + 矩阵高速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自己主动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就能够得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自己主动机有一些特别,根节点是为空串,然而 // 每走一步的时候,假设没法走了,这时候,不一定是回到根 // 节点,由于有可能单个的字符

POJ 2778 AC自动机+矩阵幂 不错的题

http://poj.org/problem?id=2778 有空再重新做下,对状态图的理解很重要 题解: http://blog.csdn.net/morgan_xww/article/details/7834801 另外做了矩阵幂的模板: //ac.sz是矩阵的大小 void mulmtr(long long x[MAXNODE][MAXNODE],long long y[MAXNODE][MAXNODE])//y=x*y { ll tmp[MAXNODE][MAXNODE]; for(in

POJ 2778 DNA Sequence

DNA Sequence Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 277864-bit integer IO format: %lld      Java class name: Main It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's

POJ 2778 DNA Sequence(AC自动机+矩阵快速幂)

题目链接:http://poj.org/problem?id=2778 题意:有m种DNA序列是有疾病的,问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列.(仅含A,T,C,G四个字符) 思路:Trie图的状态转移,用矩阵mat[i][j]来表示从结点i到j只走一步有几种走法,那么mat的n次幂就表示从结点i到j走n步有几种走法,题目要求解的就是从头节点走n步且不包含危险结点的走法. mat = mat^n   ans = (mat[0][0] + mat[0][1] + ...

POJ 2778 DNA Sequence(AC自动机+矩阵)

[题目链接] http://poj.org/problem?id=2778 [题目大意] 给出一些字符串,求不包含这些字符串的长度为n的字符串的数量 [题解] 我们将所有串插入自动机计算match,对于自动机上所有节点构建转移矩阵, 对于得到的可达矩阵我们求n长路的数量,统计0到各个点的n长路之和就是答案. [代码] #include <cstdio> #include <cstring> using namespace std; const int N=110; typedef

poj 2778 (Aho-Corasick &amp; 矩阵优化) - xgtao -

题目链接 给出m(m<=10)个长度不超过10的'A''T''G''C'序列,求长度为n(n<=2*1e9)的'A''T''G''C'序列不含上述m个序列中的任意一个序列的种类数. 首先出现了多个模板串,考虑Aho-Corasick,n的范围提示出要使用log级别的算法,并且能在Trie树上使用,矩阵是很好的选择,矩阵在有向图中的意义,可以求出s-t的方法数,因为矩阵的计算过程刚好满足乘法原理和加法原理,i行j列表示,i-j的方法数,例如以下矩阵 |0,1,1,0|   |0,0,0,1|这个