poj 2778 (Aho-Corasick & 矩阵优化) - 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|这个矩阵就表示以下图形,

                                        |0,1,0,1|如果问从i,走到j走N步的方式,

                                      |0,0,0,0|那么就只需要矩阵n次幂,并输出i行j列的数字

然而Aho-Corasick实在Trie上实现的,而Trie是一个有向的图(树),那么就构建矩阵,并输出root节点编号的矩阵那一行的总和。在得到fail指针的时候要把整个图建好。

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <queue>
#include <cstring>
#include <algorithm>
#define LL long long//***
using namespace std;
const int N = 110;
const int mod = 100000;
int n,m,ncnt;
char ill[15];

struct node{
	int id;
	bool flag;
	node *ch[4],*fail;
	void init(){
		flag = false;
		for(int i = 0;i < 4;++i)ch[i] = NULL;
	}
}trie[N*N];
int hash[128];//***
struct Matrix{
	LL map[N][N];
	void clear(){
		memset(map,0,sizeof(map));
	}
}c;

node *newnode(){
	node *p = &trie[ncnt];
	p->init();
	p->id = ncnt++;
	return p;
}

void insert(node *root,char *s){
	node *p = root;
	while(*s != ‘\0‘){
		if(p->ch[hash[*s]])p = p->ch[hash[*s]];
		else {
			p->ch[hash[*s]] = newnode();
			p = p->ch[hash[*s]];
		}
		++s;
	}
	p->flag = true;
}

void Build(node *root){
	queue <node *> q;
	q.push(root);
	while(!q.empty()){
		node *p = q.front();q.pop();
		for(int i=0;i<4;++i){
            if(p->ch[i]){
                node *next = p->fail;
                while(next && !next->ch[i])next = next->fail;
                p->ch[i]->fail = next ? next->ch[i]:root;
                if(p->ch[i]->fail->flag)p->ch[i]->flag=true;//***
                q.push(p->ch[i]);
            }
			else p->ch[i] = (p == root) ? root:p->fail->ch[i];
            if(!p->ch[i]->flag)++c.map[p->id][p->ch[i]->id];
        }
    }
}

Matrix Matrix_mul(Matrix x,Matrix y){
	Matrix res;
	res.clear();
	for(int i = 0;i < ncnt;++i){
		for(int j = 0;j < ncnt;++j){
			for(int k = 0;k < ncnt;++k){
				res.map[i][j] = (res.map[i][j]+y.map[i][k]*x.map[k][j])%mod;
			}
		}
	}
	return res;
}

Matrix Pow(Matrix x,int n){
	Matrix res;
	res.clear();
	for(int i = 0;i < ncnt;++i)res.map[i][i] = 1;
	while(n){
		if(n&1)res = Matrix_mul(res,x);
		x = Matrix_mul(x,x);
		n >>=1;
	}
	return res;
}

void _fre(node *p){
	for(int i = 0;i < 4;++i){
		if(p->ch[i])_fre(p->ch[i]);
	}
	free(p);
}

int main(){
	hash[‘A‘] = 0,hash[‘C‘] = 1,hash[‘G‘] = 2,hash[‘T‘] = 3;
	while(scanf("%d%d",&m,&n) != EOF){
		ncnt = 0;
		c.clear();
		memset(trie,0,sizeof(trie));
		node *root = newnode();
		for(int i = 0;i < m;++i){
			scanf("%s",ill);
			insert(root,ill);
		}
		Build(root);
		Matrix res = Pow(c,n);
		LL ans = 0;
		for(int i = 0;i < ncnt;++i){
			ans += res.map[0][i];
			ans %= mod;
		}
		cout<<ans<<endl;
	//	_fre(root);莫名RE
	}
	return 0;
}

  

时间: 2024-10-07 20:16:14

poj 2778 (Aho-Corasick & 矩阵优化) - xgtao -的相关文章

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

DNA Sequence POJ - 2778 AC 自动机 矩阵乘法

定义重载运算的时候一定要将矩阵初始化,因为这个调了一上午...... Code: #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<string> #define maxn 100000 typedef long long ll; using namespace std; void setIO(string a){ freopen((a+

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

题目:传送门 题意: 给你m个病毒串,只由(A.G.T.C) 组成, 问你生成一个长度为 n 的 只由 A.C.T.G 构成的,不包含病毒串的序列的方案数. 解: 对 m 个病毒串,建 AC 自动机, 然后, 这个AC自动机就类似于一张有向图, 可以用邻接矩阵存这张有向图. 最多10个病毒串, 每个病毒串长度不超过 10, 那最多是个 100 * 100 的矩阵, 可以接受. 最后用矩阵快速幂加速推导. #include<cstdio> #include<cstring> #inc

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 3150] Cellular Automaton (矩阵快速幂 + 矩阵乘法优化)

Cellular Automaton Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 3048   Accepted: 1227 Case Time Limit: 2000MS Description A cellular automaton is a collection of cells on a grid of specified shape that evolves through a number of dis