【AC自动机+状压dp】HDU2825 Wireless Password

题意 : 输入n(1<=n<=25)、m(0<=m<=10)、k,意思就是给你 m 个模式串,问你构建长度为 n 至少包含 k 个模式串的方案有多少种 mod20090717

 

直接进行计数一定是比较麻烦的,又发现m比较小,所以我们可以进行一下状态压缩再继续进行计算

记dp[i][k][s]为长度为i,在AC自动机上j位,拥有的模式串集合为s的方案数

此外AC自动机上每个点还有处理出来一个当前包含哪些模式串的集合

这个dp还要滚动一下数组,要不然会T!!

 

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k,tot;
const int mod=20090717;
struct ac
{
	int fail,son[26],end,state;
}tr[105];
int dp[2][105][1030];
void insert(char *ss,int cnt)
{
	int u=0,len=strlen(ss);
	for(int i=0;i<len;i++)
	{
		int dig=ss[i]-'a';
		if(!tr[u].son[dig]) tr[u].son[dig]=++tot;
		u=tr[u].son[dig];
	}
	tr[u].state|=(1<<cnt);
}
void build()
{
	queue <int> q;
	for(int i=0;i<26;i++) if(tr[0].son[i]) q.push(tr[0].son[i]);
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		tr[u].state|=tr[tr[u].fail].state;
		for(int i=0;i<26;i++)
		{
			if(tr[u].son[i])
			{
				tr[tr[u].son[i]].fail=tr[tr[u].fail].son[i];
				q.push(tr[u].son[i]);
			}
			else tr[u].son[i]=tr[tr[u].fail].son[i];
		}
	}
}
int check(int x)
{
	int res=0;
	for(int i=0;i<n;i++)
		if(x&(1<<i))
			res++;
	if(res>=k) return 1;
	return 0;
}
int DP()
{
	memset(dp,0,sizeof(dp));
	dp[0][0][0]=1;
	int cur=1; 
	for(int i=0;i<n;i++)
	{
		memset(dp[cur],0,sizeof(dp[cur]));
		for(int j=0;j<=tot;j++)
			for(int st=0;st<(1<<m);st++)
				if(dp[cur^1][j][st])
					for(int dig=0;dig<26;dig++)
					{
						int to=tr[j].son[dig];
						dp[cur][to][st|tr[to].state]=(dp[cur][to][st|tr[to].state]+dp[cur^1][j][st])%mod; 
					}
		cur^=1;
	}
	int ans=0;
	for(int st=0;st<(1<<m);st++)
		if(check(st)==1)
		{
			for(int i=0;i<=tot;i++)
				ans=(ans+dp[cur^1][i][st])%mod;
		}
	return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	while(scanf("%d%d%d",&n,&m,&k) && (n || m || k))
	{
		memset(tr,0,sizeof(tr));
		tot=0; char s[11];
		for(int i=1;i<=m;i++)
		{
			scanf("%s",s);
			insert(s,i-1);
		}
		build();
		printf("%d\n",DP());
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值