题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2825

题意:给一些字符串,构造出长度为n的字符串,它至少包含k个所给字符串,求能构造出的个数。

题解:

对end[]节点标记数组进行改动,用二进制下第几位表示即为包含第几个给定子串;

状态转移方程为:dp[i+1][x][k|end[x]]=(dp[i+1][x][k|end[x]]+dp[i][j][k])%mod;(没懂为什么要用‘ | ’ ,等搞明白了在更新吧,,)

dp[i][j][l] 表示长度 i 在第j个结点且当前包含字符串集合为 l 的方案数;

  1 #include<bits/stdc++.h>
  2 #define ll long long
  3 using namespace std;
  4 
  5 const ll mod=20090717;
  6 const ll maxn = 111;
  7 ll ans=0;
  8 ll sum[1100];
  9 ll n,m,k;
 10 
 11 struct tire{
 12     ll nxt[maxn][30],fail[maxn],end[maxn],tot,root,vis[maxn];
 13     ll newNode(){
 14         for(ll i=0;i<26;i++) nxt[tot][i] = -1;
 15         end[tot++] = 0;
 16         return tot-1;
 17     }
 18     void Init(){
 19         tot = 0;
 20         root = newNode();
 21         memset(dp,0,sizeof(dp));
 22     }
 23     void Insert(char *buf,ll id){
 24         ll len = strlen(buf),i,u = root;
 25         for(i=0;i<len;i++){
 26             ll x = buf[i]-'a';
 27             if(nxt[u][x]==-1) nxt[u][x] = newNode();
 28             u = nxt[u][x];
 29         }
 30         end[u] |= 1<<id;
 31     }
 32     void build(){
 33         queue <ll> q;
 34         fail[root] = root;
 35         for(ll i=0;i<26;i++){
 36             if(nxt[root][i]==-1) nxt[root][i] = root;
 37             else{
 38                 fail[nxt[root][i]] = root;
 39                 q.push(nxt[root][i]);
 40             }
 41         }
 42         while(!q.empty()){
 43             ll now = q.front();
 44             q.pop();
 45             for(ll i=0;i<26;i++){
 46                 if(nxt[now][i]==-1) nxt[now][i] = nxt[fail[now]][i];
 47                 else{
 48                     fail[nxt[now][i]] = nxt[fail[now]][i];
 49                     q.push(nxt[now][i]);
 50                 }
 51             }
 52             end[now]|=end[fail[now]];
 53         }
 54     }
 55     ll dp[30][111][1<<10];
 56     void solve(){
 57         dp[0][0][0]=1;
 58         for(ll i=0;i<n;i++){
 59             for(ll j=0;j<tot;j++){
 60                 for(ll k=0;k<(1<<m);k++){
 61                     if(dp[i][j][k]){
 62                         for(ll u=0;u<26;u++){
 63                             ll x=nxt[j][u];
 64                             dp[i+1][x][k|end[x]]+=dp[i][j][k];
 65                             dp[i+1][x][k|end[x]]%=mod;
 66                         }
 67                     }
 68                 }
 69             }
 70         }
 71         ans=0;
 72         for(ll i=0;i<tot;i++){
 73             for(ll j=0;j<(1<<m);j++){
 74                 if(dp[n][i][j]&&sum[j]>=k) ans=(ans+dp[n][i][j])%mod;
 75             }
 76         }
 77         cout<<ans<<endl;
 78     }
 79 }ac;
 80 
 81 char s1[maxn];
 82 
 83 int main()
 84 {
 85     for(ll i=0;i<(1<<10);i++){
 86         for(ll j=0;j<10;j++){
 87             if(i&(1<<j)) sum[i]++;
 88         }
 89     }
 90     while(cin>>n>>m>>k){
 91         if(!n&&!m&&!k) break;
 92         ac.Init();
 93         for(ll i=0;i<m;i++){
 94             cin>>s1;
 95             ac.Insert(s1,i);
 96         }
 97         ac.build();
 98         ac.solve();
 99     }
100     return 0;
101 }