题目链接:  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[],*fail;
int cnt;
}pool[maxn],*root,*sz;
int dp[][maxn],now,pre;
Trie *newnode()
{
Trie *ret=sz++;
memset(ret->next,,sizeof(ret->next));
ret->fail=;
ret->cnt=;
return ret;
}
int idx(char c)
{
if(c=='A') return ;
if(c=='G') return ;
if(c=='C') return ;
if(c=='T') return ;
}
void Insert(string str)
{
Trie *pos=root;
for(int i=;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=;c<;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=;c<;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=;
string tt;
while(cin>>n&&n)
{
init();
for(int i=;i<=n;i++)
{
cin>>tt;
Insert(tt);
}
getfail();
cin>>tt;
int cnt=sz-pool;
now=,pre=;
memset(dp,0x3f,sizeof(dp));
dp[now][]=;
for(int i=;i<tt.size();i++)
{
now^=,pre^=;
memset(dp[now], 0x3f, sizeof(dp[now]));
for(int j=;j<cnt;j++)
{
if(dp[pre][j]<inf)
{
Trie *pos=pool+j;
for(int k=;k<;k++)
{
if(pos->next[k]->cnt) continue;
int t=pos->next[k]-pool;
int add=idx(tt[i])==k?:;
dp[now][t]=min(dp[now][t],dp[pre][j]+add);
}
}
}
}
int ans=inf;
for(int i=;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);
}
}
13518359 neopenx 3691 Accepted 232K 63MS C++ 2696B 2014-10-10 22:10:31

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

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

    题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2-> ...

  2. hdu 4057(ac自动机+状态压缩dp)

    题意:容易理解... 分析:题目中给的模式串的个数最多为10个,于是想到用状态压缩dp来做,它的状态范围为1-2^9,所以最大为2^10-1,那我们可以用:dp[i][j][k]表示长度为i,在tri ...

  3. bzoj1195 神奇的ac自动机+状态压缩dp

    /* 难的不是ac自动机,是状态压缩dp 之前做了一两题类似题目,感觉理解的还不够透彻 */ #include<iostream> #include<cstdio> #incl ...

  4. HDU 4057 Rescue the Rabbit ( AC自动机 + 状态压缩DP )

    模板来自notonlysuccess. 模式串只有10个,并且重复出现的分值不累加,因此很容易想到状态压缩. 将模式串加入AC自动机,最多有10*100个状态. dp[i][j][k]:串长为i,在T ...

  5. 计蒜客-蒜场抽奖(AC自动机+状态压缩DP)

    题解:题意不再说了,题目很清楚的. 思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串.AC自动机中fail指针是指当前后缀在其他串里面所能 ...

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

    题意:给你两个串A,B, 问一个串长为M+N且包含A和B且恰好包含M个R的字符串有多少种组合方式,所有字符串中均只含有字符L和R. dp[i][j][k][S]表示串长为i,有j个R,在自动机中的状态 ...

  7. hdu 2825(ac自动机+状态压缩dp)

    题意:容易理解... 分析:在做这道题之前我做了hdu 4057,都是同一种类型的题,因为题中给的模式串的个数最多只能为10个,所以我们就很容易想到用状态压缩来做,但是开始的时候我的代码超时了dp时我 ...

  8. POJ 1321 棋盘问题(状态压缩DP)

    不总结的话, 同一个地方会 WA 到死 思路: 状态压缩 DP. 1. s 表示压缩状态, 若第 i 列放了棋子, 那么该列置 1, 否则该列置 0. 假如 s = 3(0x011) 那么表示棋盘的第 ...

  9. POJ 3254 Corn Fields(状态压缩DP)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4739   Accepted: 2506 Descr ...

随机推荐

  1. PHP 上传文件和读取文件崎岖路

    今天php上传文件和读取文件没有搞出来,全靠后来大神来帮忙,总结一下:主要涉及到一下几个方面,在ubuntu下mkdir文件夹的时候要注意权限问题,一般情况下php是以一个较低的权限去执行的,所以如果 ...

  2. [BZOJ1061][Noi2008]志愿者招募

    [BZOJ1061][Noi2008]志愿者招募 试题描述 申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难 题:为即将启动的奥运新项目招募一批短期志愿 ...

  3. ZeroMQ(java)中监控Socket

    基本上ZeroMQ(java)中基本的代码都算是过了一遍了吧,不过觉得它在日志这一块貌似基本没有做什么工作,也就是我们通过日志来知道ZeroMQ都发生了什么事情.. 而且由于ZeroMQ中将连接的建立 ...

  4. HDOJ 1106

    #include<iostream> #include<algorithm> #include<string.h> #include<stdlib.h> ...

  5. 【Django】Django 如何支持 分组查询、统计?

    代码: from django.db.models import Sum alarm_sum_group_items = models.FILE_PROTECT_ALARM.objects.filte ...

  6. 【Hadoop】HIVE 小结概览

    一.HIVE概览小结 二.HIVE安装 Hive只在一个节点上安装即可 .上传tar包 .解压 tar -zxvf hive-.tar.gz -C /cloud/ .配置mysql metastore ...

  7. 【转】基于LDA的Topic Model变形

    转载自wentingtu 基于LDA的Topic Model变形最近几年来,随着LDA的产生和发展,涌现出了一批搞Topic Model的牛人.我主要关注了下面这位大牛和他的学生:David M. B ...

  8. linux下创建库函数

    来源: 在Linux下如何使用自己的库函数-riverok-ChinaUnix博客 http://blog.chinaunix.net/uid-21393885-id-88128.html 构建Lin ...

  9. delphi 换行操作 Word

    delphi 换行操作 我将我的商用<旅行社管理系统>的 发团通知 部分奉献给您,望对您有所帮助. procedure TFrmMain.N327Click(Sender: TObject ...

  10. 2.saltstack笔记之目标,模块,返回写入数据库

    作者:刘耀 QQ:22102107 一.目标(targeting Minions) 1.匹配Minions Id 匹配所有 (*) [root@node1 salt]# salt '*' test.p ...