poj 2778 DNA Sequence

题目传送门

题意:

给出m个DNA序列,求出长度为n的不含上述序列的个数。(0 <= m <= 10),(1 <= n <=2000000000)

题解:

首先我们应将DNA序列存入Tire中,之后可以构造一个矩阵A,A[i][j]表示从i节点状态是否可以一步转移到j节点状态。构造方法则很明显:沿着fail指针进行构造,如果它是被标记的序列那么就置为0。所以在BuildFail是要将没有儿子节点通过fail指针指向前面的节点。之后求出A^n求出方案数,ans=sigma(A[0][i])。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

inline char idx(const char &c) {
	switch (c) {
		case 'A' : return 0;
		case 'T' : return 1; 
		case 'C' : return 2; 
		case 'G' : return 3; 
	}
}

//AC-auto
const int MS = 110, Siz = 4;
int ch[MS][Siz], root, totN, Fail[MS];
bool Unable[MS];

int NewNode() {
	Unable[++totN] = 0;
	memset(ch[totN], 0, sizeof ch[totN]);
	return totN;
}

void Insert(char *S) {
	int cur = root;
	char id;
	while (*S) {
		id = idx(*S);
		if (ch[cur][id]) {
			cur = ch[cur][id];
		} else {
			cur = ch[cur][id] = NewNode();
		}
		++S;
	}
	Unable[cur] = 1;
}

void BuildFail() {
	queue<int>q;
	Fail[root] = root;
	for (int i = 0; i < Siz; ++i) if (ch[root][i]) {
		Fail[ch[root][i]] = root;
		q.push(ch[root][i]);
	}
	int f, p;
	while (!q.empty()) {
		p = q.front(); q.pop();
		if (Unable[Fail[p]]) Unable[p] = 1;
		for (int i = 0; i < Siz; ++i) 
		if (ch[p][i]) {
			f = Fail[p];
			
			while (!ch[f][i] && f) f = Fail[f];
			
			if (ch[f][i]) Fail[ch[p][i]] = ch[f][i];
			else Fail[ch[p][i]] = root;
			
			q.push(ch[p][i]);
		} else {
			ch[p][i] = ch[Fail[p]][i];//构建假儿子
		}
		
	}
	
}

//Matrix

typedef long long LL;
const int Mod = 100000;

LL tmp1[MS][MS], tmp2[MS][MS];

void Matmul(LL A[MS][MS], LL B[MS][MS]){
	memset(tmp1, 0, sizeof tmp1);
	for (int i = 0; i <= totN; ++i)
	for (int j = 0; j <= totN; ++j) 
	for (int k = 0; k <= totN; ++k) 
	tmp1[i][j] = (tmp1[i][j] + A[i][k] * B[k][j]) % Mod;;

	memcpy(A, tmp1, sizeof tmp1);
}

void MatPow(LL A[MS][MS], int n) {
		for (int i = totN; ~i; --i)
		for (int j = totN; ~j; --j)
		tmp2[i][j] = (i == j);
		while (n) {
			if (n & 1) Matmul(tmp2, A);
			Matmul(A, A);
			n >>= 1;
		}
		memcpy(A, tmp2, sizeof tmp2);
	}

//

void BuildMat(LL A[MS][MS]) {
	for (int i = 0; i <= totN; ++i)
	for (int j = 0; j < Siz; ++j)
	if (!Unable[i] && !Unable[ch[i][j]])//不是被标记的点才可以连接
	++A[i][ch[i][j]];
}


char tmpS[12];

int main() {
	int n, m, tot;
	LL ans[MS][MS];
	while (scanf("%d%d\n", &m, &n) == 2) {
		totN = -1; root = NewNode();
		while (m--) {
			gets(tmpS);
			Insert(tmpS);
		}
		BuildFail();
		memset(ans, 0, sizeof ans);
		BuildMat(ans);
		
		for (int i = 0; i <= totN; ++i) {
			for (int j = 0; j <= totN; ++j)
			printf("%d ", ans[i][j]);
			puts("");
		}
		tot = 0;
		MatPow(ans, n);
		for (int i = 0; i <= totN; ++i) {
			tot += ans[0][i];
			tot %= Mod;
		}
		printf("%d\n", tot);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值