这道题的话用到了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. node中从express到koa再到koa2的发展历程

    koa是Express的下一代基于Node.js的web框架,目前有1.x和2.0两个版本. 历史 1. Express Express是第一代最流行的web框架,它对Node.js的http进行了封 ...

  2. 理解urllib、urllib2及requests区别及运用

    urllib and urllib2 区别 –博主提示:下面的是python2中的用法,python3需要做出相应修改. urllib和urllib2模块都做与请求URL相关的操作,但他们提供不同的功 ...

  3. 6.使用Feign实现声明式REST调用

                        使用Feign实现声明式REST调用 6.1. Feign简介 Feign是一个声明式的REST客户端,它的目的就是让REST调用更加简单. Feign提供了H ...

  4. LVS之NAT模式

    目录: 网络环境 LVS服务器配置 LVS服务器添加ipvs规则 RS服务器配置 访问验证 抓包分析 注意事项 [网络环境] 服务器类型 IP 说明 lvs_vip 192.168.2.130 vip ...

  5. Qt Delgate的使用 简单说明

    (一) Qt Model/View 的简单说明 .预定义模型 (二)使用预定义模型 QstringListModel例子 (三)使用预定义模型QDirModel的例子 (四)Qt实现自定义模型基于QA ...

  6. ArcGIS Python 坐标系信息

    ############################################################# import arcpy import os from arcpy impo ...

  7. 7.Mahout菩萨

    1.Maout简介 2.机器学习介绍 3.Mahout算法介绍

  8. vs2017中信号与槽连接

    在vs2012里和在Qt Creator里添加信号和槽不一样,这里把两种环境下怎么添加详细说明一下 1.在vs2012里添加信号和槽 新建一个qt的项目QtDemo 在qtdeom.h里添加槽 pri ...

  9. 摘抄java基础

    1.Java中Static的相关用法总结?(静态方法:静态变量:静态代码块) public static void main(String args[])执行的关键,在于有static.有了stati ...

  10. Docker镜像搭建ubuntu下samba目录共享

    第一种方法:(未使用) yum install docker // 下载镜像 docker pull dperson/samba // 启动镜像,具体看文档,但重要的配置是以下的注释 docker r ...