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

题意:

给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;
}
时间: 2024-11-23 16:44:16

[AC自动机+状压dp] hdu 2825 Wireless Password的相关文章

[AC自动机+状压dp] hdu 4534 郑厂长系列故事——新闻净化

题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现.权值在-999~999之间. 然后必须出现的串不超过8个.然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大.输出次数和权值. 然后根据样例,那些必须出现的串,其实权值是0. 思路: 很明显一开始建自动机构成trie图,但是需要注意的就是mark和sum的更新.个人是把所有中间的节点的sum全部赋值成了-inf. 接着只有8个必须出现的串,所以

hdu 2825 aC自动机+状压dp

Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5640    Accepted Submission(s): 1785 Problem Description Liyuan lives in a old apartment. One day, he suddenly found that there

HDU 3341 Lost&#39;s revenge(AC自动机+状压DP)

Lost's revenge Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 4548    Accepted Submission(s): 1274 Problem Description Lost and AekdyCoin are friends. They always play "number game"(A bor

poj 1699 Best Sequence(AC自动机+状压DP)

题目链接:poj 1699 Best Sequence 题目大意:给定N个DNA序列,问说最少多长的字符串包含所有序列. 解题思路:AC自动机+状压DP,先对字符串构造AC自动机,然后在dp[s][i]表示匹配了s,移动到节点i时候的最短步数. #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <iostream> #include &

【HDU3341】 Lost&#39;s revenge (AC自动机+状压DP)

Lost's revenge Time Limit: 5000MS Memory Limit: 65535KB 64bit IO Format: %I64d & %I64u Description Lost and AekdyCoin are friends. They always play "number game"(A boring game based on number theory) together. We all know that AekdyCoin is t

hdu 2825 Wireless Password(AC自动机+状压DP)

题目链接:hdu 2825 Wireless Password 题目大意:N,M,K,M个字符串作为关键码集合,现在要求长度为N,包含K个以上的关键码的字符串有多少个. 解题思路:AC自动机+dp,滚动数组,因为关键码个数不会超过10个,所以我们用二进制数表示匹配的状态.dp[i][j][k] 表示到第i个位置,j节点,匹配k个字符串. #include <cstdio> #include <cstring> #include <queue> #include <

【AC自动机】【状压dp】hdu2825 Wireless Password

f(i,j,S)表示当前字符串总长度为i,dp到AC自动机第j个结点,单词集合为S时的方案数. 要注意有点卡常数,注意代码里的注释. #include<cstdio> #include<cstring> #include<queue> using namespace std; queue<int>q; #define MOD 20090717; int child[111][26],fail[111],size,f[30][111][1100],tag[11

hdu 4057 Rescue the Rabbit(AC自动机+状压dp)

题目链接:hdu 4057 Rescue the Rabbit 题意: 给出一些模式串,每个串有一定的价值,现在构造一个长度为M的串,问最大的价值为多少,每个模式串最多统计一次. 题解: 由于每个模式串最多统计一次,所以我们要考虑记录是否已经存在该串. 考虑dp[i][j][k]表示当前考虑到i的长度,存在的串的组合为j,在AC自动机上走到了k这个节点的状态. 然后转移一下就能将所有能到达的状态走到.然后取一个最大值就行了. 1 #include<bits/stdc++.h> 2 #defin

AC自动机+状压DP

HDU 2825 题目大义: 给定m个长度不超过10的单词,你需要构造一个长度为n的文本串,文本串包含的给定单词不小于k个,求方案数. 分析: 看完题目觉得像是数学题,需要一些容斥啥的算算(其实是瞎想,不知道怎么实现),最后也只会暴力搜索判断这种没分数的写法.其实是一道DP题,算是一种利用AC自动机做DP的套路,需要学会. 定义F[i][j][mask],表示当前文本串长度为i,匹配到了AC自动机上的第j个节点,已包含的单词集合为mask时的方案数.显然我们可以从j节点向下更新,继续枚举一个l字