zoukankan      html  css  js  c++  java
  • 【HDU2825】Wireless Password【AC自动机,状态压缩DP】

    题意

      题目给出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
    ",ans%MOD);
     98     }
     99 return 0;
    100 }
    View Code
  • 相关阅读:
    5步教你完成小熊派开发板贴片
    了解JS压缩图片,这一篇就够了
    【华为云推官招募】加入云推官,月入8万的兼职不是梦
    JavaScript中的正则表达式详解
    一瓶可乐的自动售货机指令“旅程”
    年近而立,Java何去何从?
    数据平台、大数据平台、数据中台……你确定能分得清吗?
    微软看上的Rust 语言,安全性真的很可靠吗
    云图说丨手把手教你为容器应用配置弹性伸缩策略
    Spark优化之小文件是否需要合并?
  • 原文地址:https://www.cnblogs.com/LQLlulu/p/9344802.html
Copyright © 2011-2022 走看看