Hdu 2457 DNA repair (字符串_AC自…

http://acm.hdu.edu.cn/showproblem.php?pid=2457

题目的大意:给定n个危险DNA序列,再给一段长度长为L的DNA序列S,
DNA序列S中可能包含危险DNA序列,可以改变S中的字符,改变一个
算一次操作,问最少操作几次可使S不含危险DNA序列并输出,
如果怎么操作都会含有危险DNA序列输出-1。

 

解题思路:采用AC自动机+dp。想法比较创新,用给定的n个
危险DNA序列,建立一个Trie 树,每个树的节点都可以看做状态
转移方程的一个 状态。即只要当前节点不为 危险节点(某个
危险DNA序列的结束位置),则此状态可取。


状态转移方程 为 dp[i][j->next[k]] = min(dp[i][j->next[k]],dp[i-1][j] + (S[i] != k))
(dp[i][j]表示在我们构造解的过程中,长度为i且到节点 j位置的最少操作数,
不可达到值为inf) .


或者 我们这样看,根据危险DNA序列所建的字典树,我们用模拟的方式 ,从第一个
字符开始构造, 依次递增,找到一个满足要求的字符串,在构造此字符串的同时,比较
该字符串和输入要判断的序列S,若该位置i的字符和s[i],相同,则表示,此位置
的字符不需要改变,反则,需要把s[i]该为词字符,为一次改变操作.
所以此题中,由字典树构造失败指针时,需要考虑考虑所有存在的next 节点,
即 temp->next[i] =NULL时,需对temp->next[i]的指向赋值,使其充当自身next节点
的失败指针的作用,确保匹配失败是可以回溯到相应节点的位置。


注意 动态规划方程中 每模拟增加一个字符,就是从j 状态节点,转到 j->next[k]状态节点 ,
所以  dp[i][j->next[k]] 可由 dp[i-1][j] + (S[i] != k) 得到。
所以 最后 的结果应该是 搜 dp[len][j],0<=j<count 的最小值

 

 

//=+
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define kind 4
#define MAX 10001
#define inf 99999
struct node
{
    int flag;
    int num ;
    node *next[kind];
    node *fail;
};
node *q[MAX],tre[MAX];
char keyword[21];
char str[1001];
int dp[1010][MAX],head,tail,count;
node *root;
node * new_node(){ 
    node *p=(struct node *)malloc(sizeof(struct node));
    p=&tre[count];    
    p->flag = 0;
    p->num  = count++;
    p->fail = NULL; 
    memset(p->next,NULL,sizeof(p->next));  
    return p; 

int min(int x,int y)
{
    if(x<y)return x;
    else return y;
}
int trs(char a)
{
    if(a=='A')return 0;
    if(a=='T')return 1;
    if(a=='G')return 2;
    if(a=='C')return 3;
}
void insert(char *word)
{
     int index,len;
     node *p = root,*newnode;
     len = strlen(word);
     for(int i=0 ;i <len  ; i++ )
     
         index=trs(word[i]);
         if(!p->next[index])
            p->next[index]=new_node();
         p=p->next[index];//指针移动至下一层
         if(p->flag)break;
     }
     p->flag=1;  //单词结尾 节点 flag =1 做标记  
}
void build_ac_automation()
{
     head=0;tail=1;
     q[head]=root;
     node *temp,*p;
     while(head!=tail)
     {
         temp=q[head++];
         p=NULL;
         for(int i=0;i< kind ;i ++)
         {
             if(temp->next[i])
             {
                 if(temp==root)temp->next[i]->fail=root;
                 else {
                         temp->next[i]->fail=temp->fail->next[i];      
                       if (temp->fail->next[i]->flag == 1)
                 //说明从root 到 节点temp->next[i]的字符串中 包含子串 从root
                  // 到节点 temp->fail->next[i]的危险DNA序列,故此节点 标记为 1,
                 // 动态规划时不能到此状态。
                           temp->next[i]->flag = 1;
                                                           
                 q[tail++]=temp->next[i];
             }
             else
             {
                if(temp==root)temp->next[i]=root;
                else temp->next[i]=temp->fail->next[i];
             }
                       
     }
}
int query()
{
    int i,j,k,index,len=strlen(str);
    node *p;
    for(i=0;i<=len;i++)
      for(j=0;j<count;j++)
          dp[i][j]=inf;
    dp[0][0]=0;
    for(i=1; i <= len ;i ++)
    {
       index=trs(str[i-1]);
       for(j=0;j< count ;j++)
         if(dp[i-1][j]<inf){
            for(k=0;k<4;k++)
            {
              if(tre[j].next[k]->flag==0)
              {
                   p=tre[j].next[k];
                   int tt=0;
                   if(index!=k)tt=1;
                   dp[i][p->num]=min(dp[i][p->num],dp[i-1][j]+tt);
              }
           }
       }
    }
    int ans=inf;
    for(j=0;j< count ;j++)
      if(dp[len][j]<ans)ans=dp[len][j];
    if(ans==inf)return -1;
    else return ans;
}
int main()
{
    int i,t,n,a,cases=1;
   // freopen("1.txt","r",stdin);
  //  freopen("2.txt","w",stdout);
    while(1)
    {
       scanf("%d",&n);
       if(n==0)break;
       count=0;
       root=new_node();
       for(i=0;i<n;i++)
       {
           scanf("%s",keyword);
          // printf("--%s\n",keyword);
           insert(keyword);
       }
       build_ac_automation();
       scanf("%s",str);
       a=query();
       printf("Case %d: %d\n",cases++,a);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值