Wireless Password HDU - 2825(AC自动机,状压DP)

Wireless Password HDU - 2825(AC自动机,状压DP)

传送门

题意:给m种子串,要求长度为n的构造串中至少有k种串,求方案数。

题解:将m个串放入字典树中,然后在字典树dfs搜索所有情况,搜索到底时要判断种类是否超过k个,这里用状态压缩存储,在加记忆化即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
int n,m,ma[107][26],cnt,tk;
int fail[107];
int vis[107];
ll dp[107][26][1027];
int er[107];
const int mod=20090717;
string s;
void up(string s,int k){
	int now=0;
	for(int i=0;i<s.length();i++){
		if(ma[now][s[i]-'a']==0){
			ma[now][s[i]-'a']=++cnt;
		}
		now=ma[now][s[i]-'a'];
	}
	vis[now]|=er[k];
}
void fgo(){
	queue<int>sa;
	for(int i=0;i<=25;i++){
		if(ma[0][i]){
			sa.push(ma[0][i]);
			fail[ma[0][i]]=0;
		}
	}
	while(!sa.empty()){
		int now=sa.front();
		sa.pop();
		for(int i=0;i<=25;i++){
			if(ma[now][i]){
				sa.push(ma[now][i]);
				fail[ma[now][i]]=ma[fail[now]][i];
			}
			else{
				ma[now][i]=ma[fail[now]][i];
			}
		}
		vis[now]|=vis[fail[now]];
	}
}
ll dfs(int p,int h,int k){
	if(h==n){
		int cnt=0;
		for(int i=0;i<m;i++){
			if(k&er[i])cnt++;
		}
		if(cnt>=tk){
			return 1;
		}
		else{
			return 0;
		}
	}
	if(dp[p][h][k]!=-1){
		return dp[p][h][k];
	}
	ll ans=0;
	for(int i=0;i<=25;i++){
		int now=k;
		now|=vis[ma[p][i]];
		ans+=dfs(ma[p][i],h+1,now);
		ans%=mod;
	}
	return dp[p][h][k]=ans;
}
void init(){
	memset(vis,0,sizeof(vis));
	memset(fail,0,sizeof(fail));
	memset(dp,-1,sizeof(dp));
	memset(ma,0,sizeof(ma));
	cnt=0;
}
int main(){
	er[0]=1;
	for(int i=1;i<=10;i++){
		er[i]=er[i-1]*2;
	}
	while(1){
		init();
		scanf("%d%d%d",&n,&m,&tk);
		if(n==0&&m==0&&tk==0)break;
		for(int i=0;i<m;i++){
			cin>>s;
			up(s,i);
		}
		fgo();
		printf("%lld\n",dfs(0,0,0));
	}
}
posted @ 2020-11-26 20:11  ccsu_madoka  阅读(71)  评论(0编辑  收藏  举报