zoukankan      html  css  js  c++  java
  • HDU 2457 DNA repair(AC自动机+DP)

    DNA repair

    Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 2791    Accepted Submission(s): 1494


    Problem Description
    Biologists finally invent techniques of repairing DNA that contains segments causing kinds of inherited diseases. For the sake of simplicity, a DNA is represented as a string containing characters 'A', 'G' , 'C' and 'T'. The repairing techniques are simply to change some characters to eliminate all segments causing diseases. For example, we can repair a DNA "AAGCAG" to "AGGCAC" to eliminate the initial causing disease segments "AAG", "AGC" and "CAG" by changing two characters. Note that the repaired DNA can still contain only characters 'A', 'G', 'C' and 'T'.

    You are to help the biologists to repair a DNA by changing least number of characters.
     
    Input
    The input consists of multiple test cases. Each test case starts with a line containing one integers N (1 ≤ N ≤ 50), which is the number of DNA segments causing inherited diseases.
    The following N lines gives N non-empty strings of length not greater than 20 containing only characters in "AGCT", which are the DNA segments causing inherited disease.
    The last line of the test case is a non-empty string of length not greater than 1000 containing only characters in "AGCT", which is the DNA to be repaired.

    The last test case is followed by a line containing one zeros.
     
    Output
    For each test case, print a line containing the test case number( beginning with 1) followed by the
    number of characters which need to be changed. If it's impossible to repair the given DNA, print -1.
     
    Sample Input
    2 AAA AAG AAAG 2 A TG TGAATG 4 A G C T AGT 0
     
    Sample Output
    Case 1: 1 Case 2: 4 Case 3: -1
     
    分析:结果会随我们修改的字母不同,而出现动态的变化,所以要用到DP找到最小值
         dp[i][j]表示到字符串的第i个字母为止,当前在自动机的结点j的最小需要修改的字母数量
         这些病毒,字符串不能包含它们,也就是不能匹配到它们的最后一个字母.用end数组来记录不能被匹配的"地雷区",
         这里还要用到AC自动机 fail数组的性质,也就是不断指向拥有最长公共前缀的部分.
         比如我们的字符串现在匹配到了某个部分,字典树到了 acbc的位置上,那么我们判断这里没有以acbc结尾的病毒后,
         还需要判断是否有以 cbc  bc c结尾的病毒,那么fail数组就能帮我们到达最长公共前缀的部分。
    代码如下:
    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    #include <queue>
    using namespace std;
    #define INF 0x3f3f3f3f
    char buf[1010];
    struct Trie
    {
        int Next[1010][4];
        int fail[1010];
        bool end[1010];
        int root,L;
        int dp[1010][1010];
        int newnode()
        {
            for(int i = 0;i < 4;i++)
                Next[L][i] = -1;
            end[L++] = false;
            return L-1;
        }
        void init()
        {
            L = 0;
            root = newnode();
        }
        int get_id(char ch)
        {
            if(ch=='A')
            return 0;
            else if(ch=='G')
            return 1;
            else if(ch=='C')
            return 2;
            else if(ch=='T')
            return 3;
        }
        void insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for(int i = 0;i < len;i++)
            {
                if(Next[now][get_id(buf[i])] == -1)
                    Next[now][get_id(buf[i])] = newnode();
                now = Next[now][get_id(buf[i])];
            }
            end[now]=true;
        }
        void build()
        {
            queue<int>Q;
            fail[root] = root;
            for(int i = 0;i < 4;i++)
                if(Next[root][i] == -1)
                    Next[root][i] = root;
                else
                {
                    fail[Next[root][i]] = root;
                    Q.push(Next[root][i]);
                }
            while( !Q.empty() )
            {
                int now = Q.front();
                Q.pop();
                if(end[fail[now]])end[now]=true;//有以最小公共前缀结尾的,那么也为"地雷区";
                for(int i = 0;i < 4;i++)
                    if(Next[now][i] == -1)
                        Next[now][i] = Next[fail[now]][i];//该段的最后一个节点匹配后,跳到拥有最大公共后缀的fail节点继续匹配
                    else
                    {
                        fail[Next[now][i]]=Next[fail[now]][i];//当前节点的fail节点等于它前驱节点的fail节点的后继节点
                        Q.push(Next[now][i]);
                    }
            }
        }
        int solve(char buf[])
        {
            int minn=INF;
         int   len=strlen(buf);
            for(int i=0;i<=len;i++)
                for(int j=0;j<L;j++)
                dp[i][j]=INF;
             dp[0][0]=0;
             for(int i=0;i<len;i++)
                 for(int j=0;j<L;j++)
                 {
                   if(dp[i][j]<INF)
                   {
                    for(int k=0;k<4;k++)
                    {
                      int rr=Next[j][k];
                      if(end[rr])continue;
                      if(get_id(buf[i])==k)
                      dp[i+1][rr]=min(dp[i+1][rr],dp[i][j]);
                      else
                      dp[i+1][rr]=min(dp[i+1][rr],dp[i][j]+1);
                    }
                   }
                 }
             for(int j=0;j<L;j++)
             minn=min(minn,dp[len][j]);
             if(minn>=INF)
            return -1;
            else
            return minn;
        }
        void debug()
        {
            for(int i = 0;i < L;i++)
            {
                printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
                for(int j = 0;j < 4;j++)
                    printf("%2d",Next[i][j]);
                printf("]
    ");
            }
        }
    };
    Trie ac;
    int main()
    {
        int  t,n,ans,Case=0;;
           while(scanf("%d",&n)!=EOF)
        {
            Case++;
            if(n==0)break;
            ac.init();
           for(int i=0;i<n;i++)
           {
               scanf("%s",buf);
               ac.insert(buf);
           }
           ac.build();
             scanf("%s",buf);
             printf("Case %d: %d
    ",Case,ac.solve(buf));
        }
        return 0;
    }
     
        
     
        
     
  • 相关阅读:
    【写一个自己的js库】 5.添加修改样式的方法
    【写一个自己的js库】 4.完善跨浏览器事件操作
    js跑马灯效果
    jquery hover延时
    chart
    【写一个自己的js库】 3.添加几个处理字符串的方法
    【写一个自己的js库】 2.实现自己的调试日志
    django踩坑记录
    单元测试框架unittest-3
    单元测试框架unittest-2
  • 原文地址:https://www.cnblogs.com/a249189046/p/7577768.html
Copyright © 2011-2022 走看看