POJ2778-AC自动机,矩阵快速幂优化DP

6 篇文章 0 订阅
3 篇文章 0 订阅

POJ2778

题目描述

简要题意:给出 m m m个病毒串,问你由 A T G C ATGC ATGC构成的长度为 n n n 且不包含这些病毒串的字符串有多少个?

题解

定义 d p [ i ] [ j ] = d p [ 当 前 长 度 为 i ] [ 当 前 在 A C 自 动 机 的 第 j 个 节 点 ] = 方 案 数 dp[i][j]=dp[当前长度为i][当前在AC自动机的第j个节点]=方案数 dp[i][j]=dp[i][ACj]=.
转移方程也十分简单.
但是注意到 n n n的大小,显然会T到飞起,考虑矩阵乘法优化,其实再转化一下题意就是:全图中的 0 0 0节点到其他节点刚好经过 n n n步的方案数(求从 A A A点到 B B B点刚好经过 n n n步的方案数).
那么将整幅图转化为邻接矩阵,然后对矩阵快速幂即可.

代码

未优化

#include<bits/stdc++.h>//未优化 
#define M 2009
#define int long long
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int fail[M],val[M],cnt,f[M][201],n,m,tr[201][4],ans;
char s[20];
void build(){
	int len=strlen(s+1),u=0;
	for(int i=1;i<=len;i++){
		int c=0; 
		if(s[i]=='A') c=0;
		if(s[i]=='G') c=1;
		if(s[i]=='C') c=2;
		if(s[i]=='T') c=3;
		if(!tr[u][c]) tr[u][c]=++cnt;
		u=tr[u][c];
	}val[u]=1;
}
void getfail(){
	queue<int>q;
	for(int i=0;i<4;i++)
		if(tr[0][i]) q.push(tr[0][i]);
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=0;i<4;i++){
			if(!tr[u][i]) tr[u][i]=tr[fail[u]][i];
			else{
				int x=tr[u][i],y=fail[u];q.push(x);
				while(y&&!tr[y][i]) y=fail[y];
				fail[x]=tr[y][i],val[x]|=val[fail[x]];
			}
		}
	}
}
void DP(){
	f[0][0]=1,ans=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<=cnt;j++)
			if(f[i][j])
				for(int k=0;k<4;k++){
					int x=tr[j][k],y=val[x];
					if(val[x]) continue;
					f[i+1][x]+=f[i][j];
				}
	for(int i=0;i<=cnt;i++) ans+=f[n][i];
	printf("%lld\n",ans);
}
signed main(){
	m=read();n=read();
	for(int i=1;i<=m;i++){
		scanf("%s",s+1);
		build();
	}getfail();DP();
	return 0;
}

已优化

#include  <map>
#include  <set>
#include  <cmath>
#include  <queue>
#include  <cstdio>
#include  <vector>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
#define M 209
#define int long long
using namespace std;
int read(){
	int f=1,re=0;char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
int fail[M],val[M],cnt,n,m,tr[M][4],sum;
const int mod=1e5;
char s[20];
struct Matrix{
	int a[102][102];
	Matrix(){memset(a,0,sizeof(a));}
	Matrix operator*(const Matrix &b)const{
		Matrix res;
		for(int i=0;i<=cnt;i++)
			for(int j=0;j<=cnt;j++)
				for(int k=0;k<=cnt;k++)
					res.a[i][j]=(res.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
		return res;
	}
}base,ans;
void build(){
	int len=strlen(s+1),u=0;
	for(int i=1;i<=len;i++){
		int c;
		if(s[i]=='A') c=0;
		if(s[i]=='G') c=1;
		if(s[i]=='C') c=2;
		if(s[i]=='T') c=3;
		if(!tr[u][c]) tr[u][c]=++cnt;
		u=tr[u][c];
	}val[u]=1;
}
void getfail(){
	queue<int>q;
	for(int i=0;i<4;i++) if(tr[0][i]) q.push(tr[0][i]);
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=0;i<4;i++){
			if(!tr[u][i]) tr[u][i]=tr[fail[u]][i];
			else{
				int x=tr[u][i],y=fail[u];q.push(x);
				while(y&&!tr[y][i]) y=fail[y];
				fail[x]=tr[y][i];val[x]|=val[fail[x]];
			}
		}
	}
}
void init(){
	for(int i=0;i<=cnt;i++){
		for(int j=0;j<4;j++){
			int x=tr[i][j],y=val[x];
			if(!y&&!val[i]) base.a[x][i]++;//注意此处,不能写成base.a[x][i]=1 
		}
	}ans.a[0][0]=1;
}
void ksm(int b){
	while(b){
		if(b&1) ans=base*ans;
		base=base*base;
		b>>=1;
	}
}
void DP(){
	init();ksm(n);
	for(int i=0;i<=cnt;i++) sum=(sum+ans.a[i][0])%mod;
	printf("%lld\n",sum);
}
signed main(){
	m=read();n=read();
	for(int i=1;i<=m;i++){
		scanf("%s",s+1);
		build();
	}getfail();DP();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值