这道题的话用到了dp,一个比较简单的dp方程

1466: 【AC自动机】修改串

poj3691

时间限制: 1 Sec  内存限制: 128 MB
提交: 18  解决: 14
[提交] [状态] [讨论版] [命题人:admin]

题目描述

【题意】
给出n个模式串,然后给出一个修改串,求尽量少修改修改串,使得修改串不含有任何一个模式串,不能的话输出-1
每个串只有'A','C','G','T'四个字母
【输入格式】
有多组数据,输入以一个0结束
每组数据:
输入一个n(n<=50)
接下来n行输入n个模式串(每个模式串长度不超过20)
最后一行输入修改串(长度不超过1000)
【输出格式】
输出Case T: ans
T当前输出的是第T组数据,ans表示最少修改次数,不能修改则ans=-1
【样例输入】
2
AAA
AAG
AAAG   
2
A
TG
TGAATG
4
A
G
C
T
AGT
0
【样例输出】
Case 1: 1
Case 2: 4
Case 3: -1
我第一眼看到这道题的时候,一度怀疑是模板题,然后定睛一看,没这么简单,应该我们在修改的时候要尽可能的找位置去修改更多的字符,所以这就意味这我们可能要用到最方便的继承状态的dp(当然作为一个dp盲人,我一开始是没有想到的,只有暴力才是王道),下面看一下讲解

说难的话就不能说特别难,只是有一些细节要弄清楚,

  • 多组数据,一定要每一次询问前初始化
  • 因为只有四个字符,所以我们可以将四个字符转化为数字,这样我们在处理判断的时候就会方便一些
  • fail值初始化为-1,因为我们的f数组(也就是dp数组)0是有意义的
  • 构造失败指针的时候用到了我很久以前讲的一个继承的知识点

然后一切问题都游刃而解,剩下的就看代码的实现吧

(注释版,我已经把细节讲清楚了,所以的话可以尝试自己挑战一下,然后再poj提交)

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
struct node
{
int s,fail,cnt[];
/*只有四个数字,所以cnt不用定义这么大
fail表示失败指针,s记录模式串的结尾*/
}tr[];
int tot,list[];
char a[];
void clean(int x)/*多组数据清空树*/
{
tr[x].fail=-; tr[x].s=;/*为什么fail的初始化值要为-1呢,因为我们在构造失败指针的时候,
是把孩子节点直接继承失败指针,如果这个时候用0来区分的话,可能会炸掉*/
memset(tr[x].cnt,-,sizeof(tr[x].cnt));
}
int id(char c)/*为了方便,我们把要处理的数字都直接转化成数字*/
{
if(c=='A') return ;
if(c=='C') return ;
if(c=='G') return ;
return ;
}
void build_tree()/*建树板子*/
{
int x=; int len=strlen(a+);
for(int i=;i<=len;i++)
{
int y=id(a[i]);
if(tr[x].cnt[y]==-)
{
tr[x].cnt[y]=++tot;
clean(tot);
}
x=tr[x].cnt[y];
}
tr[x].s++;
}
void bfs()/*构造失败指针*/
{
list[]=; int head=,tail=;
while(head<=tail)
{
int x=list[head];
for(int i=;i<=;i++)
{
int son=tr[x].cnt[i];
if(son==-)/*没有孩子*/
{
if(x==) tr[x].cnt[i]=;/*这里要等于0,因为如果不等于0的话,在下面dp会炸掉*/
else tr[x].cnt[i]=tr[tr[x].fail].cnt[i];/*我在板子里面讲过是可以继承我fail指针的,这个是成立的*/
continue;
}
if(x==) tr[son].fail=;/*根节点的fail值为0*/
else
{
int j=tr[x].fail;
while(j!=-)/*这个点存在*/
{
if(tr[j].cnt[i]!=-)/*有孩子节点*/
{
tr[son].fail=tr[j].cnt[i];/*指向孩子节点,和上面的那个是一样的,可以继承*/
int tt=tr[j].cnt[i];
if(tr[tt].s!=) tr[son].s=;/*如果他的孩子节点是结尾的话,son也是作为结尾
因为继承所以一切都讲通了*/
break;
}
j=tr[j].fail;/*继续继承*/
}
if(j==-) tr[son].fail=;/*如果这个点不存在,那么x儿子的失败指针就指向根节点*/
}
list[++tail]=son;
}
head++;
}
}
int f[][],p,n,ans;
/*f数组是用来运行dp的,p是输入的模式串的个数,n是修改串的长度,ans记录答案
f[i][j]表示当前在第i位(修改串),匹配到AC自动机上(字典树)的第j个结点,
转移时,考虑添加一个字符,在AC自动机上获取添加这个结点会转移到的下一个结点(字符串匹配),并判断这样转移是否形成了一个模式串。
读到i个字符时,对应于j状态(DP的过程要两重循环i和j),要转移到son[j](j的子节点状态,在这里用k在[1,4]一重循环遍历所有可以转字符),
如果第i个字符跟所要转移到的字符相同,则代价为0,因为不需要改变;否则代价为1,因为需要改变*/
void dp()
{
for(int i=;i<=n;i++) for(int j=;j<=tot;j++) f[i][j]=; f[][]=;/*初始化*/
for(int i=;i<n;i++)
{
for(int j=;j<=tot;j++)
{
if(f[i][j]==) continue;
for(int k=;k<=;k++)/*四种状态*/
{
int son=tr[j].cnt[k];/*要转移的状态*/
if(tr[son].s) continue;/*已经是结尾就没有必要继续搜索了*/
f[i+][son]=min(f[i+][son],f[i][j]+(id(a[i+])!=k));
/*下一位如果等于要转移的状态,代价为0,否则就为1*/
}
}
}
ans=;
for(int i=;i<=tot;i++) ans=min(ans,f[n][i]);
if(ans==) ans=-;/*一遍一遍更新答案*/
}
int main()
{
int t=; while(scanf("%d",&p)!=EOF && p)/*多组数据*/
{
t++; tot=; clean();/*t组数据,多组数据初始化*/
for(int i=;i<=p;i++)
{
scanf("%s",a+);
build_tree();/*输入建树*/
}
scanf("%s",a+); n=strlen(a+);/*长度*/
bfs(); dp();/*失败标记跑一边,然后dp跑一边找答案*/
printf("Case %d: %d\n",t,ans);
}
return ;
}

Tristan Code 注释版

(非注释版,最好就按照这个学习啦)

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
struct node
{
int s,fail,cnt[];
}tr[];
int tot,list[];
char a[];
void clean(int x)
{
tr[x].fail=-; tr[x].s=;
memset(tr[x].cnt,-,sizeof(tr[x].cnt));
}
int id(char c)
{
if(c=='A') return ;
if(c=='C') return ;
if(c=='G') return ;
return ;
}
void build_tree()
{
int x=; int len=strlen(a+);
for(int i=;i<=len;i++)
{
int y=id(a[i]);
if(tr[x].cnt[y]==-)
{
tr[x].cnt[y]=++tot;
clean(tot);
}
x=tr[x].cnt[y];
}
tr[x].s++;
}
void bfs()
{
list[]=; int head=,tail=;
tr[].fail=-;
while(head<=tail)
{
int x=list[head];
for(int i=;i<=;i++)
{
int son=tr[x].cnt[i];
if(son==-)
{
if(x==) tr[x].cnt[i]=;
else tr[x].cnt[i]=tr[tr[x].fail].cnt[i];
continue;
}
if(x==) tr[son].fail=;
else
{
int j=tr[x].fail;
while(j!=-)
{
if(tr[j].cnt[i]!=-)
{
tr[son].fail=tr[j].cnt[i];
int tt=tr[j].cnt[i];
if(tr[tt].s!=) tr[son].s=;
break;
}
j=tr[j].fail;
}
if(j==-) tr[son].fail=;
}
list[++tail]=son;
}
head++;
}
}
int f[][],p,n,ans;
void dp()
{
for(int i=;i<=n;i++) for(int j=;j<=tot;j++) f[i][j]=; f[][]=;
for(int i=;i<n;i++)
{
for(int j=;j<=tot;j++)
{
if(f[i][j]==) continue;
for(int k=;k<=;k++)
{
int son=tr[j].cnt[k];
if(tr[son].s) continue;
f[i+][son]=min(f[i+][son],f[i][j]+(id(a[i+])!=k));
}
}
}
ans=;
for(int i=;i<=tot;i++) ans=min(ans,f[n][i]);
if(ans==) ans=-;
}
int main()
{
int t=; while(scanf("%d",&p)!=EOF && p)
{
t++; tot=; clean();
for(int i=;i<=p;i++)
{
scanf("%s",a+);
build_tree();
}
scanf("%s",a+); n=strlen(a+);
bfs(); dp();
printf("Case %d: %d\n",t,ans);
}
return ;
}

Tristan Code 非注释版

AC自动机练习2:修改串的更多相关文章

  1. 模板—字符串—AC自动机(多模式串,单文本串)

    模板—字符串—AC自动机(多模式串,单文本串) Code: #include <queue> #include <cstdio> #include <cstring> ...

  2. 【字符串】BZOJ上面几个AC自动机求最为字串出现次数的题目

    (一下只供自己复习用,目的是对比这几个题,所以写得不详细.需要细节的可以参考其他博主) [BZOJ3172:单词] 题目: 某人读论文,一篇论文是由许多(N)单词组成.但他发现一个单词会在论文中出现很 ...

  3. HDU 3065 病毒侵袭持续中(AC自动机(每个模式串出现次数))

    http://acm.hdu.edu.cn/showproblem.php?pid=3065 题意:求每个模式串出现的次数. 思路: 不难,把模板修改一下即可. #include<iostrea ...

  4. UVA 11019 Matrix Matcher 矩阵匹配器 AC自动机 二维文本串查找二维模式串

    链接:https://vjudge.net/problem/UVA-11019lrjP218 matrix matcher #include<bits/stdc++.h> using na ...

  5. 【AC自动机】最短母串

    [题目链接] https://loj.ac/problem/10061 [题意] 给定 n 个字符串 S1-Sn,要求找到一个最短的字符串 T,使得这 n 个字符串都是 T 的子串. [题解] 类似于 ...

  6. Aho-Corasick automaton(AC自动机)解析及其在算法竞赛中的典型应用举例

    摘要: 本文主要讲述了AC自动机的基本思想和实现原理,如何构造AC自动机,着重讲解AC自动机在算法竞赛中的一些典型应用. 什么是AC自动机? 如何构造一个AC自动机? AC自动机在算法竞赛中的典型应用 ...

  7. 算法笔记--字典树(trie 树)&& ac自动机 && 可持久化trie

    字典树 简介:字典树,又称单词查找树,Trie树,是一种树形结构,是哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较. 性质:根节点不包含字符,除根节点外每一个 ...

  8. 【AC自动机】背单词

    题意: 0 s v:添加价值为v的字符串s 1 t:查询t中含的s的权值和.(不停位置算多次) 思路: 在线AC自动机. 同学用过一个妙妙子的分块算法. 这里用二进制分组:通常用作把在线数据结构问题转 ...

  9. hdu 3695:Computer Virus on Planet Pandora(AC自动机,入门题)

    Computer Virus on Planet Pandora Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 256000/1280 ...

  10. poj_2778_DNA Sequence(AC自动机+矩阵)

    题目链接:poj_2778_DNA Sequence 题意: 有m个模式串,然后给你一个长度n,问你n长度的DNA序列有多少种不包含这m个模式串 题解: 这题显然要用AC自动机,将模式串的AC自动机建 ...

随机推荐

  1. 【Redis 设置Redis使用LRU算法】

    转自:http://ifeve.com/redis-lru/ 本文将介绍Redis在生产环境中使用的Redis的LRU策略,以及自己动手实现的LRU算法(php) 1.设置Redis使用LRU算法 L ...

  2. 【redis 学习系列】API的理解与使用(三)

    4.列表 列表(list)类型是用来存储多个有序的字符串,如图2-18所示,a.b.c.d.e五个元素从左到右组成一个有序列表,列表中的每个字符串称为元素,一个列表最大可以存储2^32-1个元素.在R ...

  3. HearthBuddy CSharpCodeProvider 如何编译cs文件

    源码 源码1 namespace Hearthbuddy.Windows{    // Token: 0x02000041 RID: 65    public class MainWindow : W ...

  4. Thinkphp5 的sesssion在同一个控制器不同的方法无法获取session的原因和对策

    这一段在用thinkPHP5开发微信小程序接口的时候,在同一个控制器一个方法中存入session,在另一个方法中取出session,一直都是无法取出. 查阅各种资料得到原因:thinkPHP5里面的s ...

  5. Hibernate3主键生成策略

    identity 采用数据库生成的主键,用于为long.short.int类型生成唯一标识, Oracle 不支持自增字段. <id name="id" column=&qu ...

  6. Git 解决冲突(3步)

    今天新人从master分支合并到自己开发分支时有冲突了,不知道怎么解决,给他解决后,顺便简单记下,免得其他人问时还要再讲一遍 1.先切到自己的分支,拉下代码 git pull 2.向某个分支发起合并 ...

  7. nginx配置, 启动命令, 反向代理配置

    2014年1月3日 13:52:07 喜欢这样的风格,干货 http://huoding.com/2013/10/23/290 -----------------下边是我自己的经验(windows)- ...

  8. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_2-1.快速搭建SpringBoot项目,采用Eclipse

    笔记 1.快速搭建SpringBoot项目,采用Eclipse     简介:使用SpringBoot start在线生成项目基本框架并导入到eclipse中 1.站点地址:http://start. ...

  9. 分布式存储ceph介绍(1)

    一.Ceph简介: Ceph是一种为优秀的性能.可靠性和可扩展性而设计的统一的.分布式文件系统.ceph 的统一体现在可以提供文件系统.块存储和对象存储,分布式体现在可以动态扩展.在国内一些公司的云环 ...

  10. python报错No module named 'pylab'

    pip instal matplotlib