这道题的话用到了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. ****题(alb)

    sol:较简单的dp题,n4随便写写,n3需要加一个小优化 int i,j,k,i1,j1,i2,j2; memset(dp,,sizeof dp); ;i<n;i+=) dp[][i][i+] ...

  2. javaScript基础用Number()把其它类型转换为Number类型

    一:基本类型 字符串 把字符串转换为数字,只要字符串中包含任意一个非有效数字字符(第一个点除外)结果都是NaN,空字符串会变为数字零 console.log(Number("12.5&quo ...

  3. Leetcode题目198.打家劫舍(动态规划-简单)

    题目描述: 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给 ...

  4. nginx 部署前端项目(vue)

    前提:安装好nginx 打开nginx目录,一般是(/usr/local/nginx) npm run build 打好vue包 一般放到(/usr/local/nginx/html/)目录下 配置: ...

  5. Notepad++格式化xml(转)

    转自:http://www.herongyang.com/XML/NPP-XML-Tools-Plugin-Download-and-Install.html Downloading and inst ...

  6. 汽车辐射监测系统-Qt开发[转]发

    功能介绍: 利用在路边的两个探测器,探测汽车的辐射剂量,通过电子板进行数据采集,并串口传输到计算机,实时显示.可以保存采集数据,进行独立显示. 开发环境: VS2008,Qt4.7, QWT 6.0. ...

  7. bind绑定服务的生命周期

    bindService(service, conn, flags); * service :意图 * conn :activity和服务的连接通道 * flags : BIND_AUTO_CREATE ...

  8. Node.js express模块 http服务

    var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('he ...

  9. 线程池使用Callable示例【我】

    实际工作中可以把下面的代码直接拿过去改改即可 package threadtest; import java.util.ArrayList; import java.util.HashMap; imp ...

  10. 九十四:CMS系统之cms后台登录限制

    装饰器,验证当前session中是否存在定义的user_id,没有就重定向到登录页 from flask import session, redirect, url_forfrom functools ...