1. 程式人生 > >【HDU2825】Wireless Password【AC自動機,狀態壓縮DP】

【HDU2825】Wireless Password【AC自動機,狀態壓縮DP】

博客 oid display 圖片 pla 遇到 當我 題目 img

題意

題目給出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 }
View Code

【HDU2825】Wireless Password【AC自動機,狀態壓縮DP】