【题解】Luogu P4045 [JSOI2009] 密码

\(dp_{i,j,S}\) 表示填了 \(i\) 位,在 AC 自动机上的 \(j\) 号节点,当前覆盖的字符串集位 \(S\) 的方案数。于是有转移:

\[\large{dp_{i,j,S}\to dp_{i+1,tr_{j,k},S\operatorname{or}sta_{tr_{j,k}}}} \]

其中 \(tr_{j,k}\) 表示 AC 自动机上 \(j\) 点加上字符 \(k\) 的节点,\(sta_j\) 表示以 \(j\) 点为结尾的字符串构成的集合,\(\operatorname{or}\) 表示按位或。

输出方案,先记忆化搜索确定每个状态 \((i,j,S)\) 能否转移到合法状态,再一遍 dfs 输出即可。

#include<bits/stdc++.h>
#define ll long long
#define il inline

using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=(1<<10)+5;
int n,m,tr[105][30],tot;
int fail[105],sta[105];
ll dp[30][105][maxn];
bool vis[30][105][maxn];
bool f[30][105][maxn];
char ans[maxn];
string s;
queue<int> q;
il bool dfs1(int i,int j,int S){
	if(vis[i][j][S]){
		return f[i][j][S];
	}
	vis[i][j][S]=1;
	if(i==m){
		return f[i][j][S]=S==(1<<n)-1;
	}
	bool &res=f[i][j][S];
	for(int k=0;k<=25;k++){
		res|=dfs1(i+1,tr[j][k],S|sta[tr[j][k]]);
	}
	return res;
}
il void dfs2(int i,int j,int S){
	if(i==m){
		for(int k=1;k<=m;k++){
			cout<<ans[k];
		}
		cout<<"\n";
		return ;
	}
	for(int k=0;k<=25;k++){
		if(f[i+1][tr[j][k]][S|sta[tr[j][k]]]){
			ans[i+1]=k+'a';
			dfs2(i+1,tr[j][k],S|sta[tr[j][k]]);
		}
	}
}
namespace cplx{
	bool end;
	il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>m>>n;
	for(int i=1,p;i<=n;i++){
		cin>>s;
		p=0;
		for(int j=0,d;j<s.size();j++){
			d=s[j]-'a';
			if(!tr[p][d]){
				tr[p][d]=++tot;
			}
			p=tr[p][d];
		}
		sta[p]|=1<<(i-1);
	}
	for(int i=0;i<=25;i++){
		if(tr[0][i]){
			q.push(tr[0][i]);
		}
	}
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=0;i<=25;i++){
			if(tr[u][i]){
				fail[tr[u][i]]=tr[fail[u]][i];
				sta[tr[u][i]]|=sta[fail[tr[u][i]]];
				q.push(tr[u][i]);
			}
			else{
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
	dp[0][0][0]=1;
	for(int i=0;i<=m;i++){
		for(int j=0;j<=tot;j++){
			for(int S=0;S<1<<n;S++){
				if(!dp[i][j][S]){
					continue;
				}
				for(int k=0;k<=25;k++){
					dp[i+1][tr[j][k]][S|sta[tr[j][k]]]+=dp[i][j][S];
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=tot;i++){
		ans+=dp[m][i][(1<<n)-1];
	}
	cout<<ans<<"\n";
	if(ans>42){
		return 0;
	}
	dfs1(0,0,0);
	dfs2(0,0,0);
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-02-06 16:24  zhangxy__hp  阅读(20)  评论(0)    收藏  举报