HDU2825-AC自动机+状压dp

HDU2825

题目描述

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

题解

首先对模式串建立AC自动机
定义 d p [ i ] [ j ] [ k ] = d p [ 当 前 长 度 为 i ] [ 当 前 在 A C 自 动 机 第 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,对于是包含模式串的限制,我们通常用状压 d p dp dp表示选了哪些串,且AC自动机的节点需要保留选串的情况(即 v a l [ ] val[] val[])
2,注意循环的内外顺序,一般情况下,字符串长度的循环都是放在外层,因为只有先计算出长度为 i i i的所有字符串状态,才能计算长度为 i + 1 i+1 i+1的所有字符串状态。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值