题意:
给定最多10个长度不超过10的子串,让构造长度为n(n<=25)的字符串使得给定的子串都在该串中出现,问这种串有多少。
分析:
暴力是26^25中可能性,那么怎样精简状态,可以定义d[ i ][ j ][ s ]为当前构造的串长为i且已经到达自动机j位置,已经生成的子串状态为s,接着往下构造所能生成的合法串有多少。
状态转移,就是直接暴力枚举一下下一个字母是谁,在自动机中做状态转移。
一开始用ac自动机做状态不是很好理解,j其实是代表着前面走到i位置,所有最大匹配在自动机中为j的串,那么转移也如此。
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> #include <queue> #include <set> #include <string> using namespace std; typedef long long ll; #define rep(i,n) for(int i=0;i<(int)n;i++) #define rep1(i,x,y) for(int i=x;i<=y;i++) const int maxnnode = 110; const int sigma_size = 26; struct Trie{ int ch[maxnnode][sigma_size]; int val[maxnnode],f[maxnnode],last[maxnnode]; int cnt; void init(){ cnt = 0; memset(ch[0],0,sizeof(ch[0])); val[0] = 0; memset(val,0,sizeof(val)); } int id(char c){return c-'a';} void insert(char* s,int x){ int n=strlen(s),u=0; rep(i,n){ int c = id(s[i]); if(!ch[u][c]){ ch[u][c]=++cnt; val[cnt]=0; memset(ch[cnt],0,sizeof(ch[cnt])); } u = ch[u][c]; } val[u] = x; } void build(){ f[0] = last[0]=0; queue<int> Q; rep(i,sigma_size){ if(ch[0][i]) Q.push(ch[0][i]),f[ch[0][i]]=last[ch[0][i]]=0; } while(!Q.empty()){ int u=Q.front(); Q.pop(); rep(i,sigma_size){ if(!ch[u][i]){ ch[u][i] = ch[f[u]][i]; continue; } int v = ch[u][i]; f[v] = ch[f[u]][i]; last[v] = (val[f[v]] ? f[v] : last[f[v]]); Q.push(v); } } } int print_(int u){ cnt = 0; while(u) { cnt|=(1<<(val[u]-1)); u = last[u]; } return cnt; } }ac; const int maxn = 26; ll d[maxn][maxnnode][(1<<10)+10]; int n,m; ll dp(){ memset(d[n],0,sizeof(d[n])); for(int j=0;j<maxnnode;j++) d[n][j][(1<<m)-1]=1; int te = ac.cnt; for(int i=n-1;i>=0;i--) rep1(j,0,te) for(int s=0;s<(1<<m);s++){ d[i][j][s] = 0; rep(k,sigma_size){ int c = ac.ch[j][k]; int ts = ac.print_(ac.val[c] ? c : ac.last[c]); d[i][j][s]+=d[i+1][c][(s|ts)]; } } return d[0][0][0]; } vector<char> aa; void print_(int i,int j,int s){ if(i == n){ rep(i,aa.size()) cout<<aa[i]; cout<<endl; return ; } rep(k,sigma_size){ int c = ac.ch[j][k]; int ts = ac.print_(ac.val[c] ? c : ac.last[c]); if(d[i+1][c][s|ts]) { aa.push_back('a'+k); print_(i+1,c,s|ts); aa.pop_back(); } } } set<string> vvis; int main() { int kase=1; vvis.clear(); while(scanf("%d %d",&n,&m)==2 && n){ vvis.clear(); ac.init(); int rm = 0; rep(i,m){ char s[30]; scanf("%s",s); string te = s ; if(!vvis.count(te)){ ac.insert(s,++rm); vvis.insert(te); } } m = rm; ac.build(); ll ans = dp(); aa.clear(); printf("Case %d: %lld suspects\n",kase++,ans); if(ans <= 42){ print_(0,0,0); } } return 0; }