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

DNA Sequence
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 15064 Accepted: 5806

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 mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don't contain those segments. 

Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G,and the length of sequences is a given integer n. 

Input

First line contains two integer m (0 <= m <= 10), n (1 <= n <=2000000000). Here, m is the number of genetic disease segment, and n is the length of sequences. 

Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10. 

Output

An integer, the number of DNA sequences, mod 100000.

Sample Input

4 3
AT
AC
AG
AA

Sample Output

36

Source

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

首先处理患病的DNA串,连接为字典树后,添加fail指针,完成AC自动机,给每个节点一个编号,然后用矩阵统计每个节点走一步可以走到的节点的种数,其中,不能包含会患病的序列,最后矩阵相乘。

在处理危险串时,注意除了危险串的末节点要舍弃以外,若某节点的last指向末节点时,也要舍弃。

还有在处理矩阵的时候要用long long,否则会出错。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 105;
const int kind = 4;
const int mod = 100000;
struct  Matrix
{
	int n, m;
	ll a[maxn][maxn];
	void clear()
	{
		n = m = 0;
		memset(a, 0, sizeof(a));
	}
	Matrix operator *(const Matrix &b) const
	{
		Matrix temp;
		temp.clear();
		temp.n = n;temp.m = b.m;
		for (int i = 0;i < n;i++)
		{
			for (int j = 0;j < b.m;j++)
			{
				for (int k = 0;k < m;k++)
					temp.a[i][j] = (temp.a[i][j]+a[i][k] * b.a[k][j]) % mod;
				temp.a[i][j] %= mod;
			}
		}
		return temp;
	}
};
Matrix pow(Matrix a, int n)		
{
	Matrix temp;
	temp.n = temp.m = a.n;
	for (int i = 0;i < a.n;i++)
	{
		for (int j = 0;j < a.n;j++)
			temp.a[i][j] = i == j ? 1 : 0;
	}
	while (n)
	{
		if (n & 1)
			temp = temp*a;
		a = a*a;
		n >>= 1;
	}
	return temp;
}
Matrix matrix;
struct node
{
	int sz, ch[maxn][kind];
	int 	val[maxn], fail[maxn],last[maxn];
	void init()
	{
		sz = 1;		 val[0] = 0;
		memset(ch[0], 0, sizeof(ch[0]));
	}
	int idx(char c)
	{
		if (c == 'A') return 0;
		if (c == 'T') return 1;
		if (c == 'C') return 2;
		if (c == 'G') return 3;
	}
	void insert(char *s,int v)
	{
		int u = 0, n = strlen(s);
		for (int i = 0;i < n;i++)
		{
			int id = idx(s[i]);
			if(!ch[u][id])
			{
				memset(ch[sz], 0, sizeof(ch[sz]));
				val[sz] = 0;
				ch[u][id] = sz++;
			}
			u = ch[u][id];
		}
		val[u] = v;
	}
	void getFail()
	{
		queue<int>q;
		fail[0] = 0;
		for (int i = 0;i < kind;i++)
		{
			int u = ch[0][i];
			if (u)
			{
				fail[u] = 0, last[u] = 0;
				q.push(u);
			}
		}
		while (!q.empty())
		{
			int r = q.front();
			q.pop(); 
			for (int i = 0;i < kind;i++)
			{
				int u = ch[r][i];
				if (!u)
				{
					ch[r][i] = ch[fail[r]][i];
					continue;
				}
				q.push(u);
				int v = fail[r];
				while (v&&!ch[v][i]) v = fail[v];
				fail[u] = ch[v][i];
				last[u] = val[fail[u]] ? fail[u] : last[fail[u]];
			}
		}
	}
	void buildMatrix()
	{
		matrix.clear();
		matrix.n = sz; matrix.m = sz;
		for (int i = 0;i < sz;i++)
		{
			if (val[i] || last[i]) continue;
			for (int j = 0;j < kind;j++)
			{
				int u = i;
				while (u&&!ch[u][j]) u = fail[u];
				u = ch[u][j];
				if(!val[u]&&!val[last[u]])
				matrix.a[i][u]++;
			}
		}
	}
};
node ac;
char P[20];
int main()
{
	ll m, n;
	while (scanf("%lld%lld", &m, &n) != EOF)
	{
		ac.init();
		for (int i = 1;i <= m;i++)
		{
			scanf("%s", P);
			ac.insert(P,i);
		}
		ac.getFail();
		ac.buildMatrix();
		Matrix ans;
		ans=pow(matrix, n);
		ll sum = 0;
		for (int i = 0;i < ac.sz;i++)		
			sum = (ans.a[0][i]+sum)%mod;		
		sum = sum%mod;
		printf("%lld\n", sum);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值