hdu 2825 Wireless Password AC自动机+状态压缩DP

题意:给定m个字符串,假设有一个长为n的密码串,其中密码串至少包含k个字符串,问这样的可能串有是多少

做法:划分状态先。以当前所在的tire树的节点,当前密码串的长度,当前所包含的串为状态,因为至多只有10个字符串,所有,就是用状态压缩好了。第i个字符串标记为2^(i-1),这一点在构建tire树,以及fial的时候要小心,每个tire树节点所蕴含的字符串要包含所有fail节点蕴含的字符串。可怜一个小错误,改了许久。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <deque>
#define CHD 26
#define mod  20090717
using namespace std;
int dp[26][103][1200];
int siz,gra[103][27],fail[103];
int word[103];
int have(int x)
{
    int ret=0;
    while(x)
    {
        ret+=x&1;
        x>>=1;
    }
    return ret;
}
void insert(char sor[],int num)
{
    int current=0,index;
    for(int i=0;sor[i];i++)
    {
        index=sor[i]-'a';
        if(!gra[current][index])
            gra[current][index]=siz++;
        current=gra[current][index];
    }
    word[current]|=1<<(num-1);
}
void build_ac(void)
{
    int current;
    deque<int> q;
    q.clear();
    for(int i=0;i<CHD;i++)
    if(gra[0][i])q.push_back(gra[0][i]);
    while(!q.empty())
    {
        current=q.front();
        q.pop_front();
        for(int i=0;i<CHD;i++)
        if(gra[current][i])
        {
            int v=gra[current][i];
            fail[v]=gra[fail[current]][i];
            word[v]|=word[fail[v]];
            q.push_back(v);
        }
        else gra[current][i]=gra[fail[current]][i];
    }
}
int solve(int len,int wk,int kk)//禁忌题目意思啊,设计参数的时候小心点
{
   int v,ans=0;
    dp[0][0][0]=1;
    for(int i=0;i<len;i++)
    {
      for(int j=0;j<siz;j++)
        for(int k=0;k<1<<wk;k++)
        if(dp[i][j][k])
          for(int t=0;t<CHD;t++)
          {
              v=gra[j][t];
              dp[i+1][v][k|word[v]]+=dp[i][j][k];
              dp[i+1][v][k|word[v]]%=mod;
          }
    }
    for(int k=0;k<1<<wk;k++)
    if(have(k)>=kk)
      for(int j=0;j<siz;j++)
      {
          ans+=dp[len][j][k];
          ans%=mod;
      }
      return ans;
}
void init(void)
{
    siz=1;
    memset(fail,0,sizeof(fail));
    memset(word,0,sizeof(word));
    memset(gra,0,sizeof(gra));
    memset(dp,0,sizeof(dp));
}
int main()
{
    int n,m,k;
    char sec[15];
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        if(0==n&&0==m&&0==k)break;
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%s",sec);
            insert(sec,i);
        }
        build_ac();
        printf("%d\n",solve(n,m,k));
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值