• AC自动机练习2:修改串


    这道题的话用到了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提交)

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<cstdlib>
      5 #include<cmath>
      6 #include<iostream>
      7 using namespace std;
      8 struct node
      9 {
     10     int s,fail,cnt[5];
     11     /*只有四个数字,所以cnt不用定义这么大
     12      fail表示失败指针,s记录模式串的结尾*/
     13 }tr[2010];
     14 int tot,list[2010];
     15 char a[2010];
     16 void clean(int x)/*多组数据清空树*/
     17 {
     18     tr[x].fail=-1; tr[x].s=0;/*为什么fail的初始化值要为-1呢,因为我们在构造失败指针的时候,
     19     是把孩子节点直接继承失败指针,如果这个时候用0来区分的话,可能会炸掉*/
     20     memset(tr[x].cnt,-1,sizeof(tr[x].cnt));
     21 }
     22 int id(char c)/*为了方便,我们把要处理的数字都直接转化成数字*/
     23 {
     24     if(c=='A') return 1;
     25     if(c=='C') return 2;
     26     if(c=='G') return 3;
     27     return 4;
     28 }
     29 void build_tree()/*建树板子*/
     30 {
     31     int x=0; int len=strlen(a+1);
     32     for(int i=1;i<=len;i++)
     33     {
     34         int y=id(a[i]);
     35         if(tr[x].cnt[y]==-1)
     36         {
     37             tr[x].cnt[y]=++tot;
     38             clean(tot);
     39         }
     40         x=tr[x].cnt[y];
     41     }
     42     tr[x].s++;
     43 }
     44 void bfs()/*构造失败指针*/
     45 {
     46     list[0]=1; int head=1,tail=1;
     47     while(head<=tail)
     48     {
     49         int x=list[head];
     50         for(int i=1;i<=4;i++)
     51         {
     52             int son=tr[x].cnt[i];
     53             if(son==-1)/*没有孩子*/
     54             {
     55                 if(x==0) tr[x].cnt[i]=0;/*这里要等于0,因为如果不等于0的话,在下面dp会炸掉*/
     56                 else tr[x].cnt[i]=tr[tr[x].fail].cnt[i];/*我在板子里面讲过是可以继承我fail指针的,这个是成立的*/
     57                 continue;
     58             }
     59             if(x==0) tr[son].fail=0;/*根节点的fail值为0*/
     60             else
     61             {
     62                 int j=tr[x].fail;
     63                 while(j!=-1)/*这个点存在*/
     64                 {
     65                     if(tr[j].cnt[i]!=-1)/*有孩子节点*/
     66                     {
     67                         tr[son].fail=tr[j].cnt[i];/*指向孩子节点,和上面的那个是一样的,可以继承*/
     68                         int tt=tr[j].cnt[i];
     69                         if(tr[tt].s!=0) tr[son].s=1;/*如果他的孩子节点是结尾的话,son也是作为结尾
     70                         因为继承所以一切都讲通了*/
     71                         break;
     72                     }
     73                     j=tr[j].fail;/*继续继承*/
     74                 }
     75                 if(j==-1) tr[son].fail=0;/*如果这个点不存在,那么x儿子的失败指针就指向根节点*/
     76             }
     77             list[++tail]=son;
     78         }
     79         head++;
     80     }
     81 }
     82 int f[2100][2100],p,n,ans;
     83 /*f数组是用来运行dp的,p是输入的模式串的个数,n是修改串的长度,ans记录答案
     84 f[i][j]表示当前在第i位(修改串),匹配到AC自动机上(字典树)的第j个结点,
     85 转移时,考虑添加一个字符,在AC自动机上获取添加这个结点会转移到的下一个结点(字符串匹配),并判断这样转移是否形成了一个模式串。 
     86 读到i个字符时,对应于j状态(DP的过程要两重循环i和j),要转移到son[j](j的子节点状态,在这里用k在[1,4]一重循环遍历所有可以转字符),
     87 如果第i个字符跟所要转移到的字符相同,则代价为0,因为不需要改变;否则代价为1,因为需要改变*/
     88 void dp()
     89 { 
     90     for(int i=0;i<=n;i++) for(int j=0;j<=tot;j++) f[i][j]=999999999; f[0][0]=0;/*初始化*/
     91     for(int i=0;i<n;i++)
     92     {
     93         for(int j=0;j<=tot;j++)
     94         {
     95             if(f[i][j]==999999999) continue;
     96             for(int k=1;k<=4;k++)/*四种状态*/
     97             {
     98                 int son=tr[j].cnt[k];/*要转移的状态*/
     99                 if(tr[son].s) continue;/*已经是结尾就没有必要继续搜索了*/
    100                 f[i+1][son]=min(f[i+1][son],f[i][j]+(id(a[i+1])!=k));
    101                 /*下一位如果等于要转移的状态,代价为0,否则就为1*/
    102             }
    103         }
    104     }
    105     ans=999999999;
    106     for(int i=0;i<=tot;i++) ans=min(ans,f[n][i]);
    107     if(ans==999999999) ans=-1;/*一遍一遍更新答案*/
    108 }
    109 int main()    
    110 {
    111     int t=0; while(scanf("%d",&p)!=EOF && p)/*多组数据*/
    112     {
    113         t++; tot=0; clean(0);/*t组数据,多组数据初始化*/
    114         for(int i=1;i<=p;i++)
    115         {
    116             scanf("%s",a+1);
    117             build_tree();/*输入建树*/
    118         }
    119         scanf("%s",a+1); n=strlen(a+1);/*长度*/
    120         bfs(); dp();/*失败标记跑一边,然后dp跑一边找答案*/
    121         printf("Case %d: %d
    ",t,ans);
    122     }
    123     return 0;
    124 }
    Tristan Code 注释版

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

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<cstdlib>
      5 #include<cmath>
      6 #include<iostream>
      7 using namespace std;
      8 struct node
      9 {
     10     int s,fail,cnt[5];
     11 }tr[2010];
     12 int tot,list[2010];
     13 char a[2010];
     14 void clean(int x)
     15 {
     16     tr[x].fail=-1; tr[x].s=0;
     17     memset(tr[x].cnt,-1,sizeof(tr[x].cnt));
     18 }
     19 int id(char c)
     20 {
     21     if(c=='A') return 1;
     22     if(c=='C') return 2;
     23     if(c=='G') return 3;
     24     return 4;
     25 }
     26 void build_tree()
     27 {
     28     int x=0; int len=strlen(a+1);
     29     for(int i=1;i<=len;i++)
     30     {
     31         int y=id(a[i]);
     32         if(tr[x].cnt[y]==-1)
     33         {
     34             tr[x].cnt[y]=++tot;
     35             clean(tot);
     36         }
     37         x=tr[x].cnt[y];
     38     }
     39     tr[x].s++;
     40 }
     41 void bfs()
     42 {
     43     list[0]=1; int head=1,tail=1;
     44     tr[0].fail=-1;
     45     while(head<=tail)
     46     {
     47         int x=list[head];
     48         for(int i=1;i<=4;i++)
     49         {
     50             int son=tr[x].cnt[i];
     51             if(son==-1)
     52             {
     53                 if(x==0) tr[x].cnt[i]=0;
     54                 else tr[x].cnt[i]=tr[tr[x].fail].cnt[i];
     55                 continue;
     56             }
     57             if(x==0) tr[son].fail=0;
     58             else
     59             {
     60                 int j=tr[x].fail;
     61                 while(j!=-1)
     62                 {
     63                     if(tr[j].cnt[i]!=-1)
     64                     {
     65                         tr[son].fail=tr[j].cnt[i];
     66                         int tt=tr[j].cnt[i];
     67                         if(tr[tt].s!=0) tr[son].s=1;
     68                         break;
     69                     }
     70                     j=tr[j].fail;
     71                 }
     72                 if(j==-1) tr[son].fail=0;
     73             }
     74             list[++tail]=son;
     75         }
     76         head++;
     77     }
     78 }
     79 int f[2100][2100],p,n,ans;
     80 void dp()
     81 {
     82     for(int i=0;i<=n;i++) for(int j=0;j<=tot;j++) f[i][j]=999999999; f[0][0]=0;
     83     for(int i=0;i<n;i++)
     84     {
     85         for(int j=0;j<=tot;j++)
     86         {
     87             if(f[i][j]==999999999) continue;
     88             for(int k=1;k<=4;k++)
     89             {
     90                 int son=tr[j].cnt[k];
     91                 if(tr[son].s) continue;
     92                 f[i+1][son]=min(f[i+1][son],f[i][j]+(id(a[i+1])!=k));
     93             }
     94         }
     95     }
     96     ans=999999999;
     97     for(int i=0;i<=tot;i++) ans=min(ans,f[n][i]);
     98     if(ans==999999999) ans=-1;
     99 }
    100 int main()  
    101 {
    102     int t=0; while(scanf("%d",&p)!=EOF && p)
    103     {
    104         t++; tot=0; clean(0);
    105         for(int i=1;i<=p;i++)
    106         {
    107             scanf("%s",a+1);
    108             build_tree();
    109         }
    110         scanf("%s",a+1); n=strlen(a+1);
    111         bfs(); dp();
    112         printf("Case %d: %d
    ",t,ans);
    113     }
    114     return 0;
    115 }
    Tristan Code 非注释版
  • 相关阅读:
    Centos 7安装配置NTP网络时间同步服务器
    Git忽略提交规则 .gitignore配置运维总结
    linux scp放后台执行方法
    《Linux Kernel Development》读书笔记PDF下载(2012.5.7最后更新)
    D4整体架构差不多快出来了
    c#实现IOleCommandTarget接口(原闯)
    centos7一键安装samba服务器
    R语言绘图高质量输出
    R语言与数据分析
    R语言Cairo包的使用
  • 原文地址:https://www.cnblogs.com/Tristanjiang/p/11374178.html
Copyright © 2020-2023  润新知