设 \(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 inlineusing 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();}