AC自动机+状压DP

  HDU 2825

题目大义:

  给定m个长度不超过10的单词,你需要构造一个长度为n的文本串,文本串包含的给定单词不小于k个,求方案数。

分析:

  看完题目觉得像是数学题,需要一些容斥啥的算算(其实是瞎想,不知道怎么实现),最后也只会暴力搜索判断这种没分数的写法。其实是一道DP题,算是一种利用AC自动机做DP的套路,需要学会。

  定义F【i】【j】【mask】,表示当前文本串长度为i,匹配到了AC自动机上的第j个节点,已包含的单词集合为mask时的方案数。显然我们可以从j节点向下更新,继续枚举一个l字母(0<=l<26)来匹配第i+1个字母,那么我们就可以向下递推了,假设当前状态方案数已知,那么可以累加到F【i+1】【tr j,l】【mask|sum【tr j,l】】中。sum表示到当前这个节点已经包含的单词集合,采用二进制。

  注意:sum数组在建立fail指针的同时求出。

#include<bits/stdc++.h>
using namespace std;
const int N=500;
const int mod=20090717;
int n,m,limit,tot,tr[N][30],sum[N],fail[N],f[30][150][1500],cnt[1500];
char s[N];
void init(){
    memset(tr,0,sizeof(tr));
    memset(sum,0,sizeof(sum));
    memset(fail,0,sizeof(fail));
    memset(f,0,sizeof(f));
    tot=0;
    f[0][0][0]=1;
}
void insert(int pos){
    int len=strlen(s+1),now=0;
    for(int i=1;i<=len;++i){
        int ch=s[i]-‘a‘;
        if(!tr[now][ch]) tr[now][ch]=++tot;
        now=tr[now][ch];
    }sum[now]=1<<pos;
}
void build(){
    queue<int>q;
    for(int i=0;i<26;++i){
        if(tr[0][i]) q.push(tr[0][i]),fail[tr[0][i]]=0;
    }
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=0;i<26;++i){
            if(tr[x][i]){
                int v=tr[x][i];
                fail[v]=tr[fail[x]][i];
                q.push(v);
            }else tr[x][i]=tr[fail[x]][i];
            sum[tr[x][i]]|=sum[tr[fail[x]][i]];
        }
    }
}
int count(int x){
    int res=0;
    for(;x;x>>=1){
        if(x&1) res++;
    }
    return res;
}
int main(){
    for(int i=0;i<1<<10;++i) cnt[i]=count(i);
    while(~scanf("%d%d%d",&n,&m,&limit)){
        if(!(n||m||limit)) break;
        init();
        for(int i=0;i<m;++i) scanf("%s",s+1),insert(i);
        build();
        for(int i=0;i<=n;++i){
            for(int j=0;j<=tot;++j){
                for(int k=0;k<1<<m;++k){
                    if(!f[i][j][k]) continue;
                    for(int l=0;l<26;++l){
                        int v=tr[j][l];
                        f[i+1][v][k|sum[v]]=(f[i+1][v][k|sum[v]]+f[i][j][k])%mod;
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<=tot;++i) for(int j=0;j<1<<m;++j) if(cnt[j]>=limit) ans=(ans+f[n][i][j])%mod;
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/kgxw0430/p/10255650.html

时间: 2024-10-08 00:34:50

AC自动机+状压DP的相关文章

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 &

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

【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 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

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

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

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 <

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

题目,包含所有的给定的n个DNA片段的序列的最短长度. 裸的AC自动机上的DP题. dp[S][u]表示已经包含的DNA片段集合为S,且当前后缀状态是自动机第u个结点的最短长度 dp[0][0]=0 我为人人+队列轻松转移.. 1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 #define INF (

BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )

建AC自动机后, dp(x, y, s)表示当前长度为x, 在结点y, 包括的串的状态为s的方案数, 转移就在自动机上走就行了. 对于输出方案, 必定是由给出的串组成(因为<=42), 所以直接暴搜答案. 数据范围很小, 可以AC(复杂度懒得算了....) ------------------------------------------------------------------------------------------------ #include<cstdio> #in

hdu_2825_Wireless Password(AC自动机+状压DP)

题目链接:hdu_2825_Wireless Password 题意: 给你m个串,问长度为n至少含k个串的字符串有多少个 题解: 设dp[i][j][k]表示考虑到长度为i,第j个自动机的节点,含有k这个压缩状态的方案数,然后DP下去就行了 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;i++) 3 using namespace std; 4 5 const int mod=20090717; 6 cons

[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的方案数 然后就是一个四重循