POJ 3691 (AC自动机+状态压缩DP)

题目链接:  http://poj.org/problem?id=3691

题目大意:给定N的致病DNA片段以及一个最终DNA片段。问最终DNA片段最少修改多少个字符,使得不包含任一致病DNA。

解题思路

首先说一下AC自动机在本题中的作用。

①字典树部分:负责判断当前0~i个字符组成的串是否包含致病DNA,这部分靠字典树上的cnt标记完成。

②匹配部分:主要依赖于匹配和失配转移关系的计算,这部分非常重要,用来构建不同字符间状态压缩的转移关系(代替反人类的位运算)。

这也是必须使用AC自动机而不单单是字典树的原因。

本题的状态压缩DP思路:

因为是求最少修改的字符。我们把最终DNA片段从第一个字符开始,先枚举字典树中所有字符,作为pre状态。

然后枚举四种修改方案,作为now状态,如果该方案与当前字符的值不同,则次数+1。

如果是致病DNA则跳过。

通过一个Trie的数组池(pool)确定(0<k<4)四种修改方案之后的转移点t的位置,t=pos->next[k]-pool

这样dp[now][t]=min(dp[now][t],dp[pre][j]+0 or 1)

本来的转移方程应该是dp[i][t]=min(dp[i][t],dp[i-1][j]+0 or 1) ,这里感谢zcwwzdjn大神提供的一个滚动数组的优化,也就是now和pre的使用。

同时他的动态AC自动机的变相静态写法也很独特, 主要是pool数组用来确定字符在字典树中标号,弥补了动态写法的不足。

#include "cstdio"
#include "string"
#include "queue"
#include "cstring"
#include "iostream"
using namespace std;
#define maxn 55*25
#define inf 0x3f3f3f3f
struct Trie
{
    Trie *next[4],*fail;
    int cnt;
}pool[maxn],*root,*sz;
int dp[2][maxn],now,pre;
Trie *newnode()
{
    Trie *ret=sz++;
    memset(ret->next,0,sizeof(ret->next));
    ret->fail=0;
    ret->cnt=0;
    return ret;
}
int idx(char c)
{
    if(c==‘A‘) return 0;
    if(c==‘G‘) return 1;
    if(c==‘C‘) return 2;
    if(c==‘T‘) return 3;
}
void Insert(string str)
{
    Trie *pos=root;
    for(int i=0;i<str.size();i++)
    {
        int c=idx(str[i]);
        if(!pos->next[c]) pos->next[c]=newnode();
        pos=pos->next[c];
    }
    pos->cnt++;
}
void getfail()
{
    queue<Trie *> Q;
    for(int c=0;c<4;c++)
    {
        if(root->next[c])
        {
            root->next[c]->fail=root;
            Q.push(root->next[c]);
        }
        else root->next[c]=root;
    }
    while(!Q.empty())
    {
        Trie *x=Q.front();Q.pop();
        for(int c=0;c<4;c++)
        {
            if(x->next[c])
            {
                x->next[c]->fail=x->fail->next[c];
                x->next[c]->cnt+=x->fail->next[c]->cnt;
                Q.push(x->next[c]);
            }
            else x->next[c]=x->fail->next[c];
        }
    }
}
void init()
{
    sz=pool; //reset
    root=newnode();
}
int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int n,no=0;
    string tt;
    while(cin>>n&&n)
    {
        init();
        for(int i=1;i<=n;i++)
        {
            cin>>tt;
            Insert(tt);
        }
        getfail();
        cin>>tt;
        int cnt=sz-pool;
        now=0,pre=1;
        memset(dp,0x3f,sizeof(dp));
        dp[now][0]=0;
        for(int i=0;i<tt.size();i++)
        {
            now^=1,pre^=1;
            memset(dp[now], 0x3f, sizeof(dp[now]));
            for(int j=0;j<cnt;j++)
            {
                if(dp[pre][j]<inf)
                {
                    Trie *pos=pool+j;
                    for(int k=0;k<4;k++)
                    {
                        if(pos->next[k]->cnt) continue;
                        int t=pos->next[k]-pool;
                        int add=idx(tt[i])==k?0:1;
                        dp[now][t]=min(dp[now][t],dp[pre][j]+add);
                    }
                }
            }
        }
        int ans=inf;
        for(int i=0;i<cnt;i++) ans=min(ans,dp[now][i]);
        if(ans==inf) printf("Case %d: -1\n",++no);
        else  printf("Case %d: %d\n",++no,ans);
    }
}
时间: 2024-10-09 22:31:33

POJ 3691 (AC自动机+状态压缩DP)的相关文章

HDU 2825 Wireless Password (AC自动机 + 状态压缩DP)

题目链接:Wireless Password 解析:给 m 个单词构成的集合,统计所有长度为 n 的串中,包含至少 k 个单词的方案数. AC自动机 + 状态压缩DP. DP[i][j][k]:长度为i的字符串匹配到状态j且包含k个magic word的可能字符串个数. AC代码: #include <algorithm> #include <iostream> #include <cstdio> #include <queue> #include <

hdu 4057 AC自动机+状态压缩dp

http://acm.hdu.edu.cn/showproblem.php?pid=4057 Problem Description Dr. X is a biologist, who likes rabbits very much and can do everything for them. 2012 is coming, and Dr. X wants to take some rabbits to Noah's Ark, or there are no rabbits any more.

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

题意:这个题目和HDU2457有点类似,都是AC自动机上的状态dp,题意就是给你只含有'A','T','C','G',四个字符的子串和文本串,问你文本串如何排列才可以使得文本串中包含有更多的模式串 解题思路:我们知道了 有 num[0] 个 'A', num[1] 个 ‘T’, num[2] 个 ‘C’,num[3] 个‘G’, 我们的可以知道暴力的思路就是把所有的文本串都枚举出来然后一一匹配.我们膜拜了一下春哥以后,就可以有以下思路:  把一个串的信息压缩一下,把具有同样个数字符的串看成是同一

HDU 4511 (AC自动机+状态压缩DP)

题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2->N,但是某些段路径(注意不是某些条)是被禁止的.问从1->N的最短距离. 解题思路: AC自动机部分: 如果只是禁掉某些边,最短路算法加提前标记被禁的边即可. 但是本题是禁掉指定的路段,所以得边走边禁,需要一个在线算法. 所以使用AC自动机来压缩路段,如禁掉的路段是1->2->3,那么in

hdu 4758 Walk Through Squares(AC自动机+状态压缩DP)

题目链接:hdu 4758 Walk Through Squares 题意: 给你一个n*m的网格,现在你要从(1,1)走到(n,m),每次只能向右走或者向下走,走完后会形成一个包含R,D的序列,这个序列必须要包含题目给出的两个字符串,问有多少种方案. 题解: 由于要从(1,1)走到(n,m),所以这个形成的字符串包含R和D的数量是一定的. 现在考虑dp[u][i][j][k],表示包含两种串的组合状态,走了i个D,走了j个R,走到了k这个AC自动机的节点的方案数,然后dp一下就行了.复杂度为O

【HDU3341】AC自动机状态压缩DP,或者说hash枚举DP,-------出题人卡常数都是狗!!!!!

题意:给若干种个串,再给个主串,然后把主串打乱顺序,使得包含子串尽量多(一种可以有多个,两个之间可以部分重叠).如第一组数据,ACGT,包含AC.CG.GT,三个,输出3.第二组数据A1A2A3,包含A1A2和A2A3两个"AA",答案为2. 其实我并没有AC.我被卡常数TLE了...实在不想写这种没意义的东西了. 贴代码,待填坑. #include <queue> #include <cstdio> #include <cstring> #incl

hdu4758AC自动机+状态压缩DP

http://acm.hdu.edu.cn/showproblem.php?pid=4758 Problem Description On the beaming day of 60th anniversary of NJUST, as a military college which was Second Artillery Academy of Harbin Military Engineering Institute before, queue phalanx is a special l

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

POJ 3254 Corn Fields 状态压缩DP

题目链接:http://poj.org/problem?id=3254 思路:状态压缩DP,状态方程为dp[i][j] += (dp[i-1][k]) code: #include <stdio.h> #include <string.h> #define N 500 const int MOD = 100000000; int dp[15][N],ant[N],n,m,k,map[15]; bool ok(int x) { if(x&(x<<1))return