AC自动机练习2:修改串
这道题的话用到了dp,一个比较简单的dp方程
1466: 【AC自动机】修改串
时间限制: 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
说难的话就不能说特别难,只是有一些细节要弄清楚,
- 多组数据,一定要每一次询问前初始化
- 因为只有四个字符,所以我们可以将四个字符转化为数字,这样我们在处理判断的时候就会方便一些
- 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:修改串的更多相关文章
- 模板—字符串—AC自动机(多模式串,单文本串)
模板—字符串—AC自动机(多模式串,单文本串) Code: #include <queue> #include <cstdio> #include <cstring> ...
- 【字符串】BZOJ上面几个AC自动机求最为字串出现次数的题目
(一下只供自己复习用,目的是对比这几个题,所以写得不详细.需要细节的可以参考其他博主) [BZOJ3172:单词] 题目: 某人读论文,一篇论文是由许多(N)单词组成.但他发现一个单词会在论文中出现很 ...
- HDU 3065 病毒侵袭持续中(AC自动机(每个模式串出现次数))
http://acm.hdu.edu.cn/showproblem.php?pid=3065 题意:求每个模式串出现的次数. 思路: 不难,把模板修改一下即可. #include<iostrea ...
- UVA 11019 Matrix Matcher 矩阵匹配器 AC自动机 二维文本串查找二维模式串
链接:https://vjudge.net/problem/UVA-11019lrjP218 matrix matcher #include<bits/stdc++.h> using na ...
- 【AC自动机】最短母串
[题目链接] https://loj.ac/problem/10061 [题意] 给定 n 个字符串 S1-Sn,要求找到一个最短的字符串 T,使得这 n 个字符串都是 T 的子串. [题解] 类似于 ...
- Aho-Corasick automaton(AC自动机)解析及其在算法竞赛中的典型应用举例
摘要: 本文主要讲述了AC自动机的基本思想和实现原理,如何构造AC自动机,着重讲解AC自动机在算法竞赛中的一些典型应用. 什么是AC自动机? 如何构造一个AC自动机? AC自动机在算法竞赛中的典型应用 ...
- 算法笔记--字典树(trie 树)&& ac自动机 && 可持久化trie
字典树 简介:字典树,又称单词查找树,Trie树,是一种树形结构,是哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较. 性质:根节点不包含字符,除根节点外每一个 ...
- 【AC自动机】背单词
题意: 0 s v:添加价值为v的字符串s 1 t:查询t中含的s的权值和.(不停位置算多次) 思路: 在线AC自动机. 同学用过一个妙妙子的分块算法. 这里用二进制分组:通常用作把在线数据结构问题转 ...
- hdu 3695:Computer Virus on Planet Pandora(AC自动机,入门题)
Computer Virus on Planet Pandora Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 256000/1280 ...
- poj_2778_DNA Sequence(AC自动机+矩阵)
题目链接:poj_2778_DNA Sequence 题意: 有m个模式串,然后给你一个长度n,问你n长度的DNA序列有多少种不包含这m个模式串 题解: 这题显然要用AC自动机,将模式串的AC自动机建 ...
随机推荐
- 如何写出好的PRD(产品需求文档)(转)
作者:Cherry,2007年进入腾讯公司,一直从事互联网广告产品管理工作,目前在SNG/效果广告平台部从事效果广告的产品运营工作. PRD(Product Requirement Document, ...
- 第11组 团队Git现场编程实战
第11组 团队Git现场编程实战 组员职责分工: 前端部分: 陈郑铧:构架的搭建,前端模块开发 陈益:前端模块开发 李镇平:前端模块开发 后端部分: 沈国煜:后端模块开发 王泽鸿:后端模块开发 林铮威 ...
- 关于jenkins
启动不了时可更改端口 java -jar jenkins.war –httpPort=8090
- 影响mysql性能的因素
一.服务器硬件. CPU不够快,内存不够多,磁盘IO太慢. 对于计算密集型的应用,CPU越可能去影响系统的性能,此时,CPU和内存将越成为系统的瓶颈. 当热数据大小远远超过系统可用内存大小时,IO资源 ...
- .netcore centos配置systemctl自动启动
systemd分两种服务系统和用户服务 对应存储位路径为系统(/usr/lib/systemd/system).用户(/etc/systemd/user/) [Unit] Description=ap ...
- 问题解决:fatal error C1083: 无法打开包括文件:No such file or directory
fatal error C1083: 无法打开包括文件:No such file or directory将别的工程直接用VS2010打开出现了该问题,此时必须检查是不是: 1. 如果要引入的这些.h ...
- Oracle 对某张表中的某一列进行取余,将结果集分为多个集合
比如分为 5个集合,那么就用某一列和5 取余 ,分别可以取 余数为 0.1.2.3.4 的结果集,那么就把集合分为5个小的集合了 1.取余数为 0 的集合 select * from (select ...
- Button加在UITableViewHeaderFooterView的self.contentView上导致不能响应点击
你有没有遇到过Button加在UITableViewHeaderFooterView的self.contentView上导致不能响应点击的情况,下面记录一下我遇到的原因和解决方法: 代码如下: - ( ...
- Redis Guide
1. Redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理.它支持字符串.哈希表.列表.集合.有序集合,位图,hyperloglogs等数 ...
- C#.NET中对称和非对称加密、解密方法汇总--亲测可用
C#.NET中对称和非对称加密.解密方法汇总--亲测可用 在安全性要求比较高的系统中都会涉及到数据的加密.解密..NET为我们封装了常用的加密算法,例如:MD5,DES,RSA等.有可逆加密,也有 ...