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

题目大意:给一系列字符串,用小写字母构造出长度为n的至少包含k个字符串的字符串,求能构造出的个数。

题目分析:在AC自动机上走n步,至少经过k个单词节点,求有多少种走法。

代码如下:

# include<iostream>
# include<cstdio>
# include<queue>
# include<cstring>
# include<algorithm>
using namespace std;

typedef long long LL;

const int mod=20090717;

int ch[105][26];
int cnt,fail[105];
int val[105];

void init()
{
	cnt=0;
	memset(ch,-1,sizeof(ch));
	memset(val,0,sizeof(val));
}

int idx(char x)
{
	return x-'a';
}

void insert(char *s,int k)
{
	int len=strlen(s);
	int r=0;
	for(int i=0;i<len;++i){
		int c=idx(s[i]);
		if(ch[r][c]==-1) ch[r][c]=++cnt;
		r=ch[r][c];
	}
	val[r]|=(1<<k);
}

void getFail()
{
	queue<int>q;
	fail[0]=0;
	for(int i=0;i<26;++i){
		if(ch[0][i]==-1)
			ch[0][i]=0;
		else{
			fail[ch[0][i]]=0;
			q.push(ch[0][i]);
		}
	}
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		val[u]|=val[fail[u]];
		for(int i=0;i<26;++i){
			if(ch[u][i]==-1)
				ch[u][i]=ch[fail[u]][i];
			else{
				fail[ch[u][i]]=ch[fail[u]][i];
				q.push(ch[u][i]);
			}
		}
	}
}

char word[15];
int dp[2][105][1<<10];

int getDigt(int x,int n)
{
	int count=0;
	for(int i=0;i<n;++i)
		if(x&(1<<i)) ++count;
	return count;
}

int DP(int n,int m,int k)
{
	memset(dp,0,sizeof(dp));
	int cur=1;
	dp[0][0][0]=1;
	for(int i=0;i<n;++i){
		memset(dp[cur],0,sizeof(dp[cur]));
		for(int j=0;j<=cnt;++j){
			for(int s=0;s<(1<<m);++s) if(dp[cur^1][j][s]){	///不加if语句会超时
				for(int c=0;c<26;++c){
					int &nxt=dp[cur][ch[j][c]][s|val[ch[j][c]]];
					nxt=(nxt+dp[cur^1][j][s])%mod;
				}
			}
		}
		cur^=1;
	}
	int ans=0;
	for(int i=0;i<(1<<m);++i) if(getDigt(i,m)>=k){
		for(int j=0;j<=cnt;++j)
			ans=(ans+dp[cur^1][j][i])%mod;
	}
	return ans;
}

int main()
{
	int n,m,k;
	while(scanf("%d%d%d",&n,&m,&k)&&(n||m||k))
	{
		init();
		for(int i=0;i<m;++i){
			scanf("%s",word);
			insert(word,i);
		}
		getFail();
		printf("%d
",DP(n,m,k));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/20143605--pcx/p/5998330.html