HDU2825-AC自动机+状压dp

本文介绍如何使用AC自动机结合状态压缩动态规划解决模式串匹配问题,具体阐述了如何构建AC自动机,状态压缩DP的状态定义及转移方程,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HDU2825

题目描述

给你 mmm个模式串,问你构建长度为nnn至少包含 kkk个模式串的方案有多少种?

题解

首先对模式串建立AC自动机
定义dp[i][j][k]=dp[当前长度为i][当前在AC自动机第j个节点][当前选择了k集合的模式串(状态压缩)]dp[i][j][k]=dp[当前长度为i][当前在AC自动机第j个节点][当前选择了k集合的模式串(状态压缩)]dp[i][j][k]=dp[i][ACj][k()]=方案数
然后正常转移即可

代码

#include<bits/stdc++.h>
using namespace std;
int dp[28][110][1<<10],tr[110][26],val[110],cnt,n,m,kk,ans,fail[110];
char s[15];
const int mod=20090717;
void clear(){//多组数据注意清空
	cnt=ans=0;
	memset(tr,0,sizeof(tr));
	memset(val,0,sizeof(val));
	memset(dp,0,sizeof(dp));
	memset(fail,0,sizeof(fail));
}
int get1(int x){
	int sum=0;
	while(x){
		if(x&1) sum++;
		x>>=1;
	}return sum;
}
void build(int num){
	int len=strlen(s+1),u=0;
	for(int i=1;i<=len;i++){
		int c=s[i]-'a';
		if(!tr[u][c]) tr[u][c]=++cnt;
		u=tr[u][c];
	}val[u]=(val[u]|(1<<(num-1)));//val值的赋值
}
void getfail(){
	queue<int>q;
	for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=0;i<26;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]];//注意val值更新 
			}
		}
	}
}
void DP(){
	dp[0][0][0]=1;//注意初始化
	for(int i=0;i<n;i++)
		for(int j=0;j<=cnt;j++)//这里cnt要取 
			for(int k=0;k<(1<<m);k++){
				if(dp[i][j][k]){
					for(int l=0;l<26;l++){
						int x=tr[j][l],y=(val[x]|k);
						dp[i+1][x][y]=(dp[i+1][x][y]+dp[i][j][k])%mod;
					}
				}
			}
	for(int i=0;i<=cnt;i++)
		for(int k=0;k<(1<<m);k++){
			if(get1(k)>=kk) ans=(ans+dp[n][i][k])%mod;
			//printf("%lld\n",dp[n][i][k]);
		}
	printf("%d\n",ans%mod);
}
signed main(){
	while(scanf("%d%d%d",&n,&m,&kk)){
		if(n==0&&m==0&&kk==0) break;
		clear();
		for(int i=1;i<=m;i++){
			scanf("%s",s+1);
			build(i);
		}getfail(),DP();
	}return 0;
}

总结

1,对于是包含模式串的限制,我们通常用状压dpdpdp表示选了哪些串,且AC自动机的节点需要保留选串的情况(即val[]val[]val[])
2,注意循环的内外顺序,一般情况下,字符串长度的循环都是放在外层,因为只有先计算出长度为iii的所有字符串状态,才能计算长度为i+1i+1i+1的所有字符串状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值