标签:分享图片 display mat mes 一个 不包含 get 状态压缩 size
题意
题目给出m(m<=10)个单词,每个单词的长度不超过10且仅由小写字母组成,给出一个正整数n(n<=25)和正整数k,问有多少方法可以组成长度为n的文本且最少包含k个给出的单词。
分析
和上一个AC自动机很相似,上一篇博客是不包含任何一个单词长度为n的方案数,这个题是包含至少k个单词的方案数,而且n,m,k都非常的小。
按照前面的经验很容易想到,我们还是得先建一个AC自动机,然后把它的单词结点标记出来。与前面不同的是我们在状态转移的时候需要考虑到当前走过的结点已经包含多少单词了。所以我们想到用dp[i][j][k]来表示当前在i结点,已经走了j步,且走过了k个单词结点。但是我们发现,这样表示状态的话没有办法转移,因为当我们遇到一个单词结点的时候,我们并不知道这个单词节点前面时候已经走过被计数了。所以我们想到,要使用状态压缩dp来解决这个题目。
f[i][j][S]当前在i结点,还需要走j步,包含的结点由S通过二进制来表示。在建AC自动机的时候,用match[i]=1<<j,来表示i结点是单词j的单词结点。
f[i][j][S]=sum(f[v][j-1][S|match[v]]).其中v是结点i的儿子结点。
当j==0时,如果S中包含的单词数目>=k,则f[i][0][S]=1,否则为0
然后我们就很容易用记忆搜索解决这个问题。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <queue> 6 7 using namespace std; 8 const int maxnode=110; 9 const int MOD=20090717; 10 const int sigma_size=26; 11 int ch[maxnode][sigma_size],match[maxnode],f[maxnode]; 12 int dp[maxnode][30][(1<<10)+100],vis[maxnode][30][(1<<10)+100]; 13 int sz; 14 void init(){ 15 sz=1; 16 memset(ch[0],0,sizeof(ch[0])); 17 memset(vis,0,sizeof(vis)); 18 match[0]=0; 19 } 20 void insert(char *s,int v){ 21 int n=strlen(s),u=0; 22 for(int i=0;i<n;i++){ 23 int c=s[i]-‘a‘; 24 if(!ch[u][c]){ 25 ch[u][c]=sz; 26 memset(ch[sz],0,sizeof(ch[sz])); 27 match[sz++]=0; 28 } 29 u=ch[u][c]; 30 } 31 match[u]|=(1<<v); 32 } 33 34 void getFail(){ 35 queue<int>q; 36 f[0]=0; 37 for(int i=0;i<sigma_size;i++){ 38 int u=ch[0][i]; 39 if(u){ 40 q.push(u); 41 f[u]=0; 42 } 43 } 44 while(!q.empty()){ 45 int r=q.front();q.pop(); 46 for(int i=0;i<sigma_size;i++){ 47 int u=ch[r][i]; 48 if(!u){ 49 ch[r][i]=ch[f[r]][i]; 50 continue; 51 } 52 q.push(u); 53 int v=f[r]; 54 while(v&&!ch[v][i])v=f[v]; 55 f[u]=ch[v][i]; 56 match[u]|=match[f[u]]; 57 } 58 } 59 } 60 int n,m,k; 61 char s[20]; 62 int Count(int S){ 63 int res=0; 64 for(int i=0;i<m;i++){ 65 if(S&(1<<i)) 66 res++; 67 } 68 return res; 69 } 70 int DP(int u,int L,int S){ 71 if(vis[u][L][S]) 72 return dp[u][L][S]; 73 vis[u][L][S]=1; 74 int &ans=dp[u][L][S]; 75 ans=0; 76 if(L==0){ 77 if(Count(S)>=k){ 78 return ans=1; 79 } 80 return ans=0; 81 } 82 for(int i=0;i<sigma_size;i++){ 83 int v=ch[u][i]; 84 ans=(ans%MOD+DP(v,L-1,S|match[v])%MOD)%MOD; 85 } 86 return ans; 87 } 88 int main(){ 89 while(scanf("%d%d%d",&n,&m,&k)!=EOF&&(n||m||k)){ 90 init(); 91 for(int i=0;i<m;i++){ 92 scanf("%s",s); 93 insert(s,i); 94 } 95 getFail(); 96 int ans=DP(0,n,0); 97 printf("%d\n",ans%MOD); 98 } 99 return 0; 100 }
【HDU2825】Wireless Password【AC自动机,状态压缩DP】
标签:分享图片 display mat mes 一个 不包含 get 状态压缩 size
原文地址:https://www.cnblogs.com/LQLlulu/p/9344802.html