DNA序列编辑距离

54 阅读2分钟

题目描述

小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。

测试用例

样例1:

输入:dna1 = "AGT",dna2 = "AGCT" 输出:1

样例2:

输入:dna1 = "AACCGGTT",dna2 = "AACCTTGG" 输出:4

样例3:

输入:dna1 = "ACGT",dna2 = "TGC" 输出:3

样例4:

输入:dna1 = "A",dna2 = "T" 输出:1

样例5:

输入:dna1 = "GGGG",dna2 = "TTTT" 输出:4

解题思路:

  • 给定两个字符串dna1和dna2长度分别为n和m。
  • 定义dp[i][j]为将dna1中的前i个字符转换为dan2的前j个字符所需要的最小操作次数
  • dp数组的大小定义为dp[n+1][m+1],因为我们最终需要返回的数据为dp[n][m],代表dna1要变为dna2所需要的最小操作次数
  • 确定递推关系:
  1. 如果dna1[i-1] = dna2[j-1],则dp[i][j] = dp[i-1]j-1
  2. 如果不相等,那就有三种操作
    • 插入:dp[i][j-1]+1,(相当于直接插入dna2的第j个字符)
    • 删除:dp[i-1][j]+1,(相当于直接删除dna2的第j+1个字符)
    • 替换:dp[i-1][j-1]+1,(相当于直接将dna1的第i个字符替换成dna2的第j个字符) 综上所述,最终dp[i][j]的值要取这三种操作的最小值
  • 动态规划初始条件的确定
    • dp[i][0] = i,表示将 word1 的前 i ii 个字符转换为空字符串的操作次数(全部删除)
    • dp[0][j] = j,表示将空字符串转换为 word2 的前 j jj 个字符的操作次数(全部插入)
  • 使用双重循环填充dp数组
  • 最终返回结果应该是dp[m][n]

代码实现

public class Main {
    public static int solution(String dna1, String dna2) {
        // 编辑距离,动态规划
        int n = dna1.length();
        int m = dna2.length();
        if(n ==0 || m == 0)
            return Math.max(n, m);
        //dp数组含义dp[i][j]表示dna1的前i个字符串变为dna2的前j个字符串需要几步
        int[][] dp = new int[n+1][m+1];
        //初始化
        for(int i =0 ;i<=n;i++)
            dp[i][0] = i;
        for(int j = 0 ;j<=m;j++)
            dp[0][j] = j;


        for(int i = 1 ; i <= n ; i++){
            for(int j = 1;j<=m;j++){
                if(dna1.charAt(i-1) == dna2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1];
                else{
                    dp[i][j] = Math.min(dp[i-1][j-1],Math.min(dp[i][j-1], dp[i-1][j]))+1;
                }
            }
        }
        return dp[n][m];
    }

    public static void main(String[] args) {
        //  You can add more test cases here
        System.out.println(solution("AGCTTAGC", "AGCTAGCT") == 2);
        System.out.println(solution("AGCCGAGC", "GCTAGCT") == 4);
    }
}