码迷,mamicode.com
首页 > 其他好文 > 详细

[AC自动机+状压dp] hdu 2825 Wireless Password

时间:2014-10-15 15:00:00      阅读:176      评论:0      收藏:0      [点我收藏+]

标签:io   os   ar   for   sp   on   代码   amp   ef   

题意:

给n,m,k ,再给出m个单词

问长度为n的字符串,至少在m个单词中含有k个的组成方案有多少种。

思路:

由于m最大是10,所以可以采取状压的思想

首先建立trie图,在每个单词的结束节点标记一个mark=(1<<id),id为单词的编号

然后需要注意的,对于每个节点,应该顺着fail指针遍历一遍,

把所有的mark取一个并集。

因为就是如果单词出现包含的话,比如 she和he 我拿了she,其实等于两个都拿了。

dp[i][j][k]  i步在节点j状态k的方案数

然后就是一个四重循环了

应该是很好理解的

最后遍历在n步时,各个节点,然后判一下状态是否里面拿了不少于k个物品的个数

做一个累加就是答案了!

做dp的时候 顺便滚动了一下

代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#include"map"
#include"string"
#define inf 9999999
#define mod 20090717
using namespace std;
int triecont;
struct trie
{
    int mark,id;
    trie *next[27],*fail;
    trie()
    {
        memset(next,0,sizeof(next));
        fail=NULL;
        mark=id=0;
    }
};
trie *root,*node[123];
void init(char *v,int k)
{
    trie *p=root;
    for(int i=0; v[i]; i++)
    {
        int tep=v[i]-'a';
        if(p->next[tep]==NULL)
        {
            p->next[tep]=new trie();
            node[triecont]=p->next[tep];
            p->next[tep]->id=triecont++;
        }
        p=p->next[tep];
    }
    p->mark|=(1<<k);
}
void getac()
{
    queue<trie*>q;
    q.push(root);
    while(!q.empty())
    {
        trie *p;
        p=q.front();
        q.pop();
        for(int i=0; i<26; i++)
        {
            if(p->next[i]==NULL)
            {
                if(p==root) p->next[i]=root;
                else p->next[i]=p->fail->next[i];
            }
            else
            {
                if(p==root) p->next[i]->fail=root;
                else  p->next[i]->fail=p->fail->next[i];
                q.push(p->next[i]);
                trie *tep=p->next[i];
                int mark=tep->mark;
                while(tep!=NULL)
                {
                    mark|=tep->mark;
                    tep=tep->fail;
                }
                p->next[i]->mark=mark;
            }
        }
    }
}
int judge(int x)
{
    int ans=0;
    for(int i=0; i<10; i++)
    {
        if(x&(1<<i)) ans++;
    }
    return ans;
}
__int64 dp[2][105][1025];
int main()
{
    int n,m,num;
    while(scanf("%d%d%d",&n,&m,&num),(n+m+num))
    {
        memset(node,0,sizeof(node));
        triecont=0;
        root=new trie();
        node[triecont]=root;
        root->id=triecont++;
        if(num>m)
        {
            puts("0");
            continue;
        }
        for(int i=0; i<m; i++)
        {
            char x[12];
            scanf("%s",x);
            init(x,i);
        }
        getac();
        for(int j=0; j<triecont; j++)  for(int k=0; k<(1<<m); k++) dp[0][j][k]=0;
        dp[0][0][0]=1;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<triecont; j++)  for(int k=0; k<(1<<m); k++) dp[i%2][j][k]=0;
            for(int j=0; j<triecont; j++)
            {
                for(int k=0; k<(1<<m); k++)
                {
                    if(dp[1-i%2][j][k]==0) continue;
                    for(int l=0; l<26; l++)
                    {
                        trie *p=node[j]->next[l];
                        int tep=p->mark|k;
                        dp[i%2][p->id][tep]+=dp[1-i%2][j][k];
                        if(dp[i%2][p->id][tep]>=mod) dp[i%2][j][tep]%=mod;
                    }
                }
            }
        }
        __int64 ans=0;
        for(int i=0; i<triecont; i++)
        {
            for(int j=0; j<(1<<m); j++)
            {
                if(judge(j)>=num)
                {
                    ans+=dp[n%2][i][j];
                    if(ans>=mod) ans%=mod;
                }
            }
        }
        printf("%I64d\n",ans%mod);
    }
    return 0;
}



[AC自动机+状压dp] hdu 2825 Wireless Password

标签:io   os   ar   for   sp   on   代码   amp   ef   

原文地址:http://blog.csdn.net/wdcjdtc/article/details/40109065

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!