poj2778.DNA Sequence (Trie图 && 矩阵乘)

有一些病毒串,求长度为 L 的且不包含任意一个病毒串的字符串数量。

首先多串匹配,所以要建立 Trie 图(AC自动机)。而 L 很大,我们需要一个 O(log n) 的算法。考虑要得到一个长度为 n 的字符串,也就是在 Trie 图上走了 n 步。是不是似曾相识,很自然地就能想到矩阵乘法:首先根据 Trie图 求出 mat[ i ][ j ] ,表示从 i 节点到 j 节点走一步能到达的方案数(当然 i, j 都不能是病毒节点),然后计算这个矩阵的 n 次方,求和就能得到答案。

#include <cstdio>
#include <cstring>

using namespace std;

const int MAX_N = 105;
const int mod = 100000;

struct Trie {
	int ch[4], ed, fl;
} t[MAX_N];
int n, m, sz = 0;
char s[15];

int get_num(char c)
{
	if (c == 'A') return 0;
	if (c == 'C') return 1;
	if (c == 'T') return 2;
	if (c == 'G') return 3;
}

void insert()
{
	int u = 0, l = strlen(s + 1);
	for (int i = 1; i <= l; i ++) {
		int c = get_num(s[i]);
		if (!t[u].ch[c]) t[u].ch[c] = ++ sz;
		u = t[u].ch[c];
	}
	t[u].ed = 1;
}

void init()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i ++)
		scanf("%s", s + 1), 
		insert();	
}

int q[100005];

void get_dna()
{
	int hd = 0, tl = 0;
	t[0].fl = 0;
	for (int c = 0; c < 4; c ++)
		if (t[0].ch[c]) {
			t[t[0].ch[c]].fl = 0;
			q[++ tl] = t[0].ch[c];
		}
	while (hd < tl) {
		int r = q[++ hd];
		for (int c = 0; c < 4; c ++) {
			if (!t[r].ch[c]) {
				t[r].ch[c] = t[t[r].fl].ch[c];
				continue;
			}
			q[++ tl] = t[r].ch[c];
			int u = t[r].ch[c], v = t[r].fl;
			while (v && !t[v].ch[c]) v = t[v].fl;
			t[u].fl = t[v].ch[c];
			if (t[t[u].fl].ed) t[u].ed = 1;
		}
	}
}

struct Matrix {
	long long mt[105][105];
}a;

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

Matrix operator^(Matrix a, int k)
{
	Matrix ret;
	for (int i = 0; i <= sz; i ++) {
		for (int j = 0; j <= sz; j ++)
			if (i == j) ret.mt[i][j] = 1;
			else ret.mt[i][j] = 0;
	}
	while (k) {
		if (k & 1) ret = ret * a;
		a = a * a;
		k >>= 1;
	}
	return ret;
}

void doit()
{
	get_dna();
	for (int i = 0; i <= sz; i ++)
		for (int j = 0; j < 4; j ++)
			if (!t[i].ed && !t[t[i].ch[j]].ed)
				a.mt[i][t[i].ch[j]] ++;
	a = a ^ m;
	long long ans = 0;
	for (int i = 0; i <= sz; i ++)
		ans = (ans + a.mt[0][i]) % mod;
	printf("%I64d\n", ans);
}

int main()
{
	init();
	doit();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值