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[],*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)的更多相关文章
- HDU 4511 (AC自动机+状态压缩DP)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4511 题目大意:从1走到N,中间可以选择性经过某些点,比如1->N,或1->2-> ...
- hdu 4057(ac自动机+状态压缩dp)
题意:容易理解... 分析:题目中给的模式串的个数最多为10个,于是想到用状态压缩dp来做,它的状态范围为1-2^9,所以最大为2^10-1,那我们可以用:dp[i][j][k]表示长度为i,在tri ...
- bzoj1195 神奇的ac自动机+状态压缩dp
/* 难的不是ac自动机,是状态压缩dp 之前做了一两题类似题目,感觉理解的还不够透彻 */ #include<iostream> #include<cstdio> #incl ...
- HDU 4057 Rescue the Rabbit ( AC自动机 + 状态压缩DP )
模板来自notonlysuccess. 模式串只有10个,并且重复出现的分值不累加,因此很容易想到状态压缩. 将模式串加入AC自动机,最多有10*100个状态. dp[i][j][k]:串长为i,在T ...
- 计蒜客-蒜场抽奖(AC自动机+状态压缩DP)
题解:题意不再说了,题目很清楚的. 思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串.AC自动机中fail指针是指当前后缀在其他串里面所能 ...
- 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,在自动机中的状态 ...
- hdu 2825(ac自动机+状态压缩dp)
题意:容易理解... 分析:在做这道题之前我做了hdu 4057,都是同一种类型的题,因为题中给的模式串的个数最多只能为10个,所以我们就很容易想到用状态压缩来做,但是开始的时候我的代码超时了dp时我 ...
- POJ 1321 棋盘问题(状态压缩DP)
不总结的话, 同一个地方会 WA 到死 思路: 状态压缩 DP. 1. s 表示压缩状态, 若第 i 列放了棋子, 那么该列置 1, 否则该列置 0. 假如 s = 3(0x011) 那么表示棋盘的第 ...
- POJ 3254 Corn Fields(状态压缩DP)
Corn Fields Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4739 Accepted: 2506 Descr ...
随机推荐
- VS2008+Qt+助手 智能提示不显示、Qt关键字不高亮的解决办法【已解决】
笔者使用的开发环境是VS2008+Qt4.8.5+VAssistX,有时候会出现代码关键字不能高亮显示,并且助手的智能提示不显示.问题如下 解决的办法是在助手的选项中设置其搜索路径,助手的设置通过VS ...
- delphi 2007 远程调试
Remote debugging lets you debug a RAD Studio application running on a remote computer. Once the remo ...
- 【Spring】Spring系列1之Spring概述
概述
- 《ASP.NET1200例》统计网站访问量源代码
void Application_Start(object sender, EventArgs e) { //在应用程序启动时运行的代码 int count=0; ...
- 转关于垂直切分Vertical Sharding的粒度
垂直切分的粒度指的是在做垂直切分时允许几级的关联表放在一个shard里.这个问题对应用程序和sharding实现有着很大的影响. 关联打断地越多,则受影响的join操作越多,应用程序为此做出的妥协就越 ...
- VC++ TinyXML
下载TinyXML库文件http://sourceforge.net/projects/tinyxml/ 在TinyXML的目录找到tinystr.h, tinyxml.h,tinystr.cpp,t ...
- 3.python基础补充(集合,collection系列,深浅拷贝)
一.集合 1.集合(set): 把不同的元素组成一起形成集合,是python基本的数据类型.集合元素(set elements):组成集合的成员 python的set和其他语言类似, 是一个无序不重复 ...
- PHP---TP框架---添加数据-----有三种方式
添加数据 添加数据有三种方式: 第一种: <?php namespace Home\Controller;//这个文件的命名空间 use Think\Controller;//use使用哪一个而 ...
- Android 图标添加消息提醒
实现方法: 1. 在对应的布局放置TextView或者ImageView. 2. 用Canvas在原来Icon的bitmap基础上进行绘制 3. 利用开源项目ViewBadger进行添加,很方便,而且 ...
- ytu 2002:C语言实验——单词统计(水题)
C语言实验——单词统计 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 61 Solved: 34[Submit][Status][Web Board] ...