【POJ2778】AC自动机,DP,矩阵乘法

题意:给出n个字串表示“缺陷基因”,然后让求长度为m的基因(4^m个)中有多少个不带病。

题解:首先建立AC自动机,然后从每个节点开始选“ATGC”有四种往外转移的途径。

如:ACG,C这两个基因建一个ACauto,然后转移矩阵为下。

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

然后把危险状态删去(赋0),即基因结束节点的行和列。

然后矩阵变成

2 1 0 0 0

2 1 0 0 0

0 0 0 0 0

0 0 0 0 0

0 0 0 0 0

2 1

2 1

然后做矩阵乘法得出答案!

/*poj2778*/
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100
#define M 4
#define MOD 100000
using namespace std;

int next[N][M],fail[N],map[N][N],crs[N],root,cnt,n,m;
bool end[N],danger[N],visit[M];
char t[N];
queue<int>q;

struct mrx
{
	long long s[N][N];
}I,O;

mrx mul(mrx a,mrx b)
{
	int i,j,k;
	mrx c=I;
	for(i=0;i<=cnt;i++)
	{
		for(j=0;j<=cnt;j++)
		{
			for(k=0;k<=cnt;k++)
			{
				c.s[i][j]+=a.s[i][k]*b.s[k][j];
			}
			c.s[i][j]%=MOD;
		}
	}
	return c;
}

mrx power(mrx a,int p)
{
	mrx ans=O;
	while(p)
	{
		if(p&1)
		{
			ans=mul(ans,a);
		}
		a=mul(a,a);
		p>>=1;
	}
	return ans;
}

void init()
{
	root=cnt=0;
	memset(fail,0,sizeof(fail));
	memset(end,0,sizeof(end));
	memset(map,0,sizeof(map));
	while(!q.empty())q.pop();
}
void acauto()
{
	int i,j,u,v,temp,alp,flag;
	scanf("%d%d",&n,&m);
	crs['A']=0;
	crs['T']=1;
	crs['G']=2;
	crs['C']=3;
	for(i=1;i<=n;i++)/*insert*/
	{
		scanf("%s",t+1);
		for(temp=root,j=1;t[j];j++)
		{
			alp=crs[t[j]];
			if(!next[temp][alp])next[temp][alp]=++cnt;
			temp=next[temp][alp];
		}
		end[temp]=1;
	}
	q.push(root);/*维护fail指针*/
	while(!q.empty())
	{
		u=q.front();q.pop();
		for(i=0;i<M;i++)
		{
			v=next[u][i];
			if(v)
			{
				if(end[v])danger[v]=1;
				if(u==root)fail[v]=root;
				else
				{
					temp=fail[u];
					while(temp&&!next[temp][i])temp=fail[temp];
					fail[v]=next[temp][i];
					if(danger[fail[v]])danger[v]=1;
				}
				q.push(v);
			}
		}
	}
	for(i=0;i<=cnt;i++)
	{
		temp=i;flag=0;
		memset(visit,0,sizeof(visit));
		while(temp&&flag<M)
		{
			for(j=0;j<M;j++)if(!visit[j]&&next[temp][j])
			{
				map[i][next[temp][j]]++;
				visit[j]=1;
				flag++;
			}
			temp=fail[temp];
		}
		if(flag<M)for(j=0;j<M;j++)if(!visit[j])
		{
			map[i][next[0][j]]++;
		}
	}
	for(i=0;i<=cnt;i++)if(danger[i])
	{
		for(j=0;j<=cnt;j++)map[i][j]=map[j][i]=0;
	}
}
void handle()
{
	int i,j;
	mrx P=I;
	for(i=0;i<=cnt;i++)O.s[i][i]=1;
	for(j=0;j<=cnt;j++)for(i=0;i<=cnt;i++)
	{
		P.s[i][j]=map[j][i];
	}
	P=mul(O,power(P,m));
	long long ans=0;
	for(i=0;i<=cnt;i++)
	{
		ans+=P.s[i][0];
	}
	cout<<ans%MOD<<endl;
}

int main()
{
//	freopen("test.in","r",stdin);
	acauto();
	handle();
	return 0;
}

时间: 2024-09-29 08:12:34

【POJ2778】AC自动机,DP,矩阵乘法的相关文章

DNA Sequence(POJ2778 AC自动机dp+矩阵加速)

传送门 DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K       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 sequenc

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理)

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理) 题意:给出若干个模式串,总长度不超过40,对于某一个字符串,它有一个价值,对于这个价值的计算方法是这样的,设初始价值为V=1,假如这个串能匹配第k个模式串,则V=V*prime[k]*(i+len[k]),其中prime[k]表示第k个素数,i表示匹配的结束位置,len[k]表示第k个模式串的长度(注意,一个字符串可以多次匹配同意个模式串).问字符集为'A'-'Z'的字符,组成的所有的长为L的字符串,

[POJ2778]DNA Sequence(AC自动机 + DP + 矩阵优化)

传送门 AC自动机加DP就不说了 注意到 m <= 10,所以模式串很少. 而 n 很大就需要 log 的算法,很容易想到矩阵. 但是该怎么构建? 还是矩阵 A(i,j) = ∑A(i,k) * A(k,j),那么i到j的方案数就是j到k的方案数称k到j的方案数,那么直接矩阵快速幂即可 #include <queue> #include <cstdio> #include <cstring> #define N 100001 #define p 100000 #d

BZOJ 1009 HNOI 2008 GT考试 AC自动机+矩阵乘法

题目大意:给出一个不能出现的字符串,问长度为k的字符串有多少种. 思路:用给定串建立一个AC自动机(或者KMP随便了),然后跑矩阵乘法就行了. CODE: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int k,length,p; char s[MAX]; in

HDU 2243 考研路茫茫――单词情结 (AC自动机 + dp)

HDU 2243 考研路茫茫――单词情结 题意:给定一些词根,如果一个单词包含有词根,则认为是有效的.现在问长度不超过L的单词里面,有多少有效的单词? 思路:这道题和POJ 2778是同样的思路.POJ 2778是要找出长度为L的单词里面有多少无效的单词.那么根据同样的方法构造矩阵,然后所有无效的单词个数为 A + A^2 + ... + A^l 个.而所有单词的个数为26 + 26^2 + - + 26^l 个.两个减一下即为答案. 矩阵连乘求和:I + A^2 + A^3 + ... + A

POJ1625 Censored!(AC自动机+DP)

题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后缀状态为自动机第j个结点的合法字符串数 dp[0][0]=1 转移转移... 注意要用高精度,因为答案最多5050. 还有就是要用unsigned char,题目的输入居然有拓展的ASCII码,编码128-255. 1 #include<cstdio> 2 #include<cstring&

HDU2457 DNA repair(AC自动机+DP)

题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步)且后缀状态为AC自动机结点j的最少需要修改的基因数 转移我为人人型,从dp[i][j]向ATCG四个方向转移到dp[i+1][j'],如果结点被标记包含致病基因就不能转移. 1 #include<cstdio> 2 #include<cstring> 3 #include<que

HDU 2243 考研路茫茫——单词情结(AC自动机+DP+快速幂)

题目链接 错的上头了... 这题是DNA的加强版,26^1 +26^2... - A^1-A^2... 先去学了矩阵的等比数列求和,学的是第二种方法,扩大矩阵的方法.剩下就是各种模板,各种套. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <algorithm> #include <vector> #include &l

HDU3341 Lost&#39;s revenge(AC自动机+DP)

题目是给一个DNA重新排列使其包含最多的数论基因. 考虑到内存大概就只能这么表示状态: dp[i][A][C][G][T],表示包含各碱基个数为ACGT且当前后缀状态为自动机第i的结点的字符串最多的数论基因数 其中ACGT可以hash成一个整数(a*C*G*T+c*G*T+g*T+T),这样用二维数组就行了,而第二维最多也就11*11*11*11个. 接下来转移依然是我为人人型,我是丢进一个队列,用队列来更新状态的值. 这题果然挺卡常数的,只好手写队列,最后4500msAC,还是差点超时,代码也

poj 1625 Censored!(AC自动机+DP+高精度)

题目链接:poj 1625 Censored! 题目大意:给定N,M,K,然后给定一个N字符的字符集和,现在要用这些字符组成一个长度为M的字符串,要求不包 括K个子字符串. 解题思路:AC自动机+DP+高精度.这题恶心的要死,给定的不能匹配字符串里面有负数的字符情况,也算是涨姿势 了,对应每个字符固定偏移128单位. #include <cstdio> #include <cstring> #include <queue> #include <vector>