问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
思路
这种有许多步骤组成的问题,可以化成许多子问题解决,可以立刻联想到动态规划,动态规划的核心思想是将问题分解为子问题,并存储子问题的解以避免重复计算。
-
定义状态:
- 我们使用一个二维数组
dp,其中dp[i][j]表示将dna1的前i个字符转换为dna2的前j个字符所需的最少编辑步骤。
- 我们使用一个二维数组
-
初始化:
- 当
dna2为空时,将dna1转换为空序列需要删除dna1的所有字符,因此dp[i][0] = i。 - 当
dna1为空时,将空序列转换为dna2需要添加dna2的所有字符,因此dp[0][j] = j。
- 当
-
状态转移:
-
情况1:字符匹配
- 如果
dna1[i-1] == dna2[j-1],则不需要进行任何编辑操作,dp[i][j] = dp[i-1][j-1]。 - 这是因为当前字符已经匹配,不需要额外的编辑步骤。
- 如果
-
情况2:字符不匹配
-
如果
dna1[i-1] != dna2[j-1],则需要考虑三种编辑操作:-
插入:在
dna1中插入一个字符使其与dna2匹配,dp[i][j] = dp[i][j-1] + 1。- 这意味着我们在
dna1的末尾插入一个字符,使得dna1的前i个字符与dna2的前j-1个字符匹配,然后再加上插入操作的代价1。
- 这意味着我们在
-
删除:删除
dna1中的一个字符,dp[i][j] = dp[i-1][j] + 1。- 这意味着我们删除
dna1的第i个字符,使得dna1的前i-1个字符与dna2的前j个字符匹配,然后再加上删除操作的代价1。
- 这意味着我们删除
-
替换:将
dna1中的一个字符替换为dna2中的字符,dp[i][j] = dp[i-1][j-1] + 1。- 这意味着我们将
dna1的第i个字符替换为dna2的第j个字符,使得dna1的前i-1个字符与dna2的前j-1个字符匹配,然后再加上替换操作的代价1。
- 这意味着我们将
-
-
在字符不匹配的情况下,我们需要选择上述三种操作中代价最小的那个,即
dp[i][j] = Math.min(dp[i-1][j], Math.min(dp[i][j-1], dp[i-1][j-1])) + 1;
-
结果:
- 最终结果存储在
dp[len1][len2]中,其中len1和len2分别是dna1和dna2的长度。
- 最终结果存储在
这个方法的时间复杂度为 O(n*m),其中 n 和 m 分别是两个序列的长度。
写在最后:
其实这道题就是最长公共子序列问题的变式,最长公共子序列,最长递增子序列,最长回文子序列等都是类似的思路,定义dp[i][j]为text1的前i个字符和ext2的前j个字符的最长公共子序列的长度。