10 2 2 hello world 4 1 1 icpc 10 0 0 0 0 0
2 1 14195065
详细讲解请点击:http://blog.csdn.net/martinue/article/details/50895963(完全同类型)
ac自动机和状态压缩dp联合版本,本来在学图论和dp的我因为hdu2852和hdu4057这俩题彻底把我没学的数据结构里面的ac自动机搞懂了(无语),顺便把规范化的模板都写好了。。。
某人要去破译wifi密码。现在他已经知道密码全部由小写字母组成。且密码的长度n(1<=n<=25)。和m(0<=m<=10)个密码中可能出现的串(magic)。每个串长度不超过10.现在他已经知道密码中这m个串至少出现了k个。可以覆盖出现。现在问你密码可能有多少种。
如果做过hdu4057了这题应该很好写而且思路也明确的。dp[i][j][k]表示长度为i的走到ac自动机里面第j个节点的已经匹配好的情况为k(将k化为2进制之后1表示该位置的词存在,0表示不存在)的可能情况总数。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> using namespace std; typedef long long ll; const int CH =26,NODE = 115,MOD=20090717; int m,n,k; int idx(char x) { return x-'a'; } int ch[NODE][CH],f[NODE],val[NODE],sz,num[1<<10]; int node() { memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; return sz++; } void init() { sz=0; node(); memset(f,0,sizeof(f)); } void ins(char *s,int i) { int u=0; for(; *s; s++) { int c=idx(*s); if(!ch[u][c]) ch[u][c]=node(); u=ch[u][c]; } val[u]|=1<<i; } int queu[110]; void getfail() { int l=0,r=0; queu[r++]=0; while(l<r) { int p=queu[l++]; for(int i=0; i<CH; i++) { if(!ch[p][i]) ch[p][i]=ch[f[p]][i]; else { int q=ch[p][i]; if(p) f[q]=ch[f[p]][i]; val[q]|=val[f[q]]; queu[r++]=q; } } } } int dp[30][105][1<<10]; void solve() { memset(dp,0,sizeof(dp)); dp[0][0][0]=1; for(int i=0; i<n; i++) for(int j=0; j<sz; j++) for(int hh=0; hh<(1<<m); hh++) if(dp[i][j][hh]) for(int k=0; k<CH; k++) dp[i+1][ch[j][k]][hh|val[ch[j][k]]]+=dp[i][j][hh], dp[i+1][ch[j][k]][hh|val[ch[j][k]]]%=MOD; int ans=0; for(int j=0; j<(1<<m); j++) { if(num[j]<k)continue; for(int i=0; i<sz; i++) ans=(ans+dp[n][i][j])%MOD; } printf("%d\n",ans); } int main() { for(int i=0; i<=1<<10; i++) { num[i]=0; for(int j=0; j<10; j++) if(i&(1<<j)) num[i]++; } while(~scanf("%d%d%d",&n,&m,&k)&&n+m+k) { init(); for(int i=0; i<m; i++) { char ss[105]; scanf("%s",ss); ins(ss,i); } getfail(); solve(); } return 0; }