Poj 2778 /1625 / 3691 AC自动机上的DP

 

题目链接:http://poj.org/problem?id=2778

                    http://poj.org/problem?id=1625

                    http://poj.org/problem?id=3691

 

AC自动机用于多字符串配匹问题,而此结构中存储的是一个前缀状态的集合,通过结点之间边(失败指针、后继指针)的联系,实现通过接受输入字符达到状态的转移。而状

态的转移通常可以和动态规划联系在一起。在这些题中恰好可以用其存储字符串的前缀及这些前缀的危险性状态,即每个节点可以代表一个前缀的状态,而通过接受输入字符达

到状态的改变。在AC自动机中转移的同时,消除重复的状态,达到状态的压缩表示的目的。子节点的状态可以从其父节点处得到,所在,动态规划过程中,只处理前一状态与

当前接收输入字符后的状态间的关系。

 

在建立AC自动机时,如果保证节点的后继中没有空指针,而指向其失败指针指向节点的同一后继节点,就构成了Trie图。

 

Code:2778

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define M 128
#define Mod 100000

typedef struct{
	int c,fail;
	int nxt[4];
}Trie;

Trie T[M];
int q[M];
int head,tail;
int size;

int m,n;

int A[M][M],R[M][M];

char dna[16];

int Key(char c)
{
	int r=4;
	switch(c){
	case 'A':r--;
	case 'C':r--;
	case 'G':r--;
	case 'T':r--;
	}
	return r;
}
int NewTrie()
{
	return ++size;
}

void Insert(char *p)
{
	int a,cur=1;
	while(*p){
		a=Key(*p++);
		if(!T[cur].nxt[a])
			T[cur].nxt[a]=NewTrie();
		cur=T[cur].nxt[a];
	}
	T[cur].c++;
}
void ACAutomation()
{
	int cur,pre,i;
	head=tail=0;
	q[tail++]=1;
	while(head^tail){
		cur=q[head++];
		for(i=0;i<4;i++){
			if(!T[cur].nxt[i]) continue;
			if(cur==1) T[T[cur].nxt[i]].fail=1;
			else{
				pre=T[cur].fail;
				while(pre&&!T[pre].nxt[i]) pre=T[pre].fail;
				T[T[cur].nxt[i]].fail=pre?T[pre].nxt[i]:1;
				if(pre&&T[T[pre].nxt[i]].c)	T[T[cur].nxt[i]].c++;
			}
			q[tail++]=T[cur].nxt[i];
		}
	}
}
void GetPreMatrix()
{
	int i,j,k;
	for(i=1;i<=size;i++){
		if(T[i].c) continue;
		for(j=0;j<4;j++){
			if(T[i].nxt[j]&&!T[T[i].nxt[j]].c)
					A[i][T[i].nxt[j]]++;
			else if(!T[i].nxt[j]){
				if(i==1) A[1][1]++;
				else{
					k=i;
					while(!T[k].nxt[j]&&k!=1)
						k=T[k].fail;
					if(T[k].nxt[j]&&!T[T[k].nxt[j]].c)
						A[i][T[k].nxt[j]]++;
					else if(!T[k].nxt[j]&&k==1)
						A[i][1]++;
				}
			}
		}
	}
}
void MatrixMulti(int a[M][M],int b[M][M])
{
	int i,j,k;
	__int64 tmp;
	int c[M][M]={0};
	for(i=1;i<=size;i++){
		for(j=1;j<=size;j++){
			for(k=1;k<=size;k++){
				tmp=((__int64)a[i][k])*((__int64)b[k][j]);
				if(tmp>=Mod)
					tmp%=Mod;
				c[i][j]+=tmp;
			}
		}
	}
	for(i=1;i<=size;i++){
		for(j=1;j<=size;j++)
			a[i][j]=c[i][j];
	}
}
void GetResMatrix()
{
	int i;
	for(i=1;i<=size;i++)
		R[i][i]=1;
	while(n){
		if(n&1) MatrixMulti(R,A);
		MatrixMulti(A,A);
		n>>=1;
	}
}
int main()
{
	int res;
	int i;
	scanf("%d%d",&m,&n);
	size=1;
	while(m--){
		scanf("%s",dna);
		Insert(dna);
	}
	ACAutomation();
	GetPreMatrix();
	GetResMatrix();
	for(i=1,res=0;i<=size;i++){
		res+=R[1][i];
		if(res>=Mod) res%=Mod;
	}
	printf("%d\n",res);
	return 0;
}

 

Code:1625

void TrieGraph()
{
	int i,cur;
	head=tail=0;
	for(i=0;i<n;i++){
		if(T[1].son[i]){
			T[T[1].son[i]].fail=1;
			q[tail++]=T[1].son[i];
		}else T[1].son[i]=1;
	}
	while(head^tail){
		cur=q[head++];
		if(T[T[cur].fail].end) T[cur].end++;
		for(i=0;i<n;i++){
			if(T[cur].son[i]){
				T[T[cur].son[i]].fail=T[T[cur].fail].son[i];
				q[tail++]=T[cur].son[i];
			}
			else T[cur].son[i]=T[T[cur].fail].son[i];
		}
	}
}void DPWork()
{
	int i,j,k;
	dp[0][1][0]=1;
	for(i=1;i<=m;i++){
		for(j=1;j<=size;j++){
			if(T[j].end) continue;
			for(k=0;k<n;k++){
				if(T[T[j].son[k]].end) continue;
				Add(dp[i][T[j].son[k]],dp[i-1][j]);
			}
		}
	}
	for(i=1;i<=size;i++){
		if(T[i].end) continue;
		Add(res,dp[m][i]);
	}
} 

Code:3691

void TrieDPWork()
{
	int i,j,k,a;
	
	memset(dp,12,sizeof(dp));
	Inf=res=dp[0][0];
	
	a=Key(s[0]);
	if(!T[T[r].son[a]].end)
		dp[0][T[r].son[a]]=0;
	for(i=0;i<4;i++){
		if(a!=i&&!T[T[r].son[i]].end)
			dp[0][T[r].son[i]]=1;
	}
	for(i=1;s[i];i++){
		a=Key(s[i]);
		for(j=1;j<=size;j++){
			if(~dp[i-1][j]){
				if(!T[T[j].son[a]].end)
					dp[i][T[j].son[a]]=Min(dp[i][T[j].son[a]],dp[i-1][j]);
				
				for(k=0;k<4;k++){
					if(a!=k&&!T[T[j].son[k]].end){
						dp[i][T[j].son[k]]=Min(dp[i][T[j].son[k]],dp[i-1][j]+1);
					}
				}
			}
		}
	}
	k=i-1;
	for(i=1;i<=size;i++)
		res=Min(res,dp[k][i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值