【HDU2825】AC自动机+状压DP

HDU2825

题目大意


  •  给定?个单词,统计所有长度为?的包含至少?个单词的串的个数
  • ? ≤ 25,? ≤ 10,单词长度 ≤ 10

 

分析


  • 对读入的?个单词建出 AC 自动机
  • 令??[?][?][????]代表长度为 ?,目前匹配到 AC 自动机上编号为? 的节点,已经包含的单词集合为????的方案数
  • 假设我们目前匹配到 AC 自动机上的节点?,那么能够包含的单词是 从点?开始,沿着????指针一直到根路径上的所有单词
  • 我们可以在构建 AC 自动机的 fail 指针时预处理出每个节点到根路径 上经过单词编号集合
  • ??[?][?][????]⟶?? [?+1][?? ?,?][????|sum[?? ?,?]],?为枚举的 下一个字符

 

Code


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int mod=20090717;
const int N=500;

int tr[N][N],f[30][150][1500],end[N],sum[N],cnt[2000],fail[N],n,m,limit,ans,tot;

void init(){
	memset(sum,0,sizeof(sum));
	memset(fail,0,sizeof(fail));
	memset(tr,0,sizeof(tr));
	memset(f,0,sizeof(f));
	f[0][0][0]=1;
	ans=tot=0;
}
void insert(string s,int pos){
	int now=0,len=s.size();
	rep(i,0,len-1){
		if(!tr[now][s[i]-'a'])tr[now][s[i]-'a']=++tot;/注意!变量值会发生变化// 
		now=tr[now][s[i]-'a'];
	}
	sum[now]=1<<pos;
}
void AC(){
	queue<int> q;
	rep(i,0,25)if(tr[0][i])q.push(tr[0][i]);
	while(q.size()){
		int x=q.front();q.pop();
		rep(i,0,25){
			if(!tr[x][i])tr[x][i]=tr[fail[x]][i];
			else{
				q.push(tr[x][i]);
				fail[tr[x][i]]=tr[fail[x]][i];
			}
			sum[tr[x][i]]|=sum[tr[fail[x]][i]];/注意!变量值会发生变化// 
		}
	}
}
int count(int S){
	int num=0;
	while(S){if(S&1)num++;S>>=1;}
	return num;
}
int main()
{
	rep(i,0,1023)cnt[i]=count(i);
	while(~scanf("%d%d%d",&n,&m,&limit)&&(n||m||limit)){
		init();
		rep(i,0,m-1){
			string s;
			cin>>s;insert(s,i);
		}
		AC();//不要忘了构造fail指针 
		for(int i=0;i<=n;i++){
			for(int j=0;j<=tot;j++){
				for(int k=0;k<(1<<m);k++){
					if(!f[i][j][k])continue;
					for(int t=0;t<26;t++){
						int y=tr[j][t];
						f[i+1][y][k|sum[y]]=(f[i+1][y][k|sum[y]]+f[i][j][k])%mod;
					}
				}
			}
		}
		rep(j,0,tot)rep(k,0,(1<<m)-1)if(cnt[k]>=limit)ans=(ans+f[n][j][k])%mod;
		cout<<ans<<"\n";
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值