问题描述
给定一段受损的 DNA 碱基序列 dna1,在每次只操作一个碱基的情况下,将其以最少的操作步骤将其还原到未受损的 DNA 碱基序列 dna2。
只可以对 DNA 碱基序列中的一个碱基进行三种操作:
- 增加一个碱基
- 去除一个碱基
- 替换一个碱基
思路
动态规划(Dynamic Programming, DP)。我们定义一个二维数组 dp
,其中 dp[i][j]
表示将 dna1
的前 i
个字符转换为 dna2
的前 j
个字符所需的最小操作数。
- 初始状态:
dp[0][0] = 0
,因为将空字符串转换为空字符串不需要任何操作。dp[i][0] = i
,因为将前 i 个字符的 dna1 转换为空字符串需要 i 次删除操作。dp[0][j] = j
,因为将空字符串转换为前 j 个字符的 dna2 需要 j 次插入操作。 - 状态转移方程:
如果
dna1[i-1] == dna2[j-1]
,则dp[i][j] = dp[i-1][j-1]
,因为最后一个字符相同,不需要额外操作。 如果dna1[i-1] != dna2[j-1]
则需要考虑三种操作:- 插入操作:dp[i][j] = dp[i][j-1] + 1
- 删除操作:dp[i][j] = dp[i-1][j] + 1
- 替换操作:dp[i][j] = dp[i-1][j-1] + 1
代码
package main
import "fmt"
func solution(dna1, dna2 string) int {
// Please write your code here
len1 := len(dna1)
len2 := len(dna2)
var dp [][]int = make([][]int, len1+1)
for i := 0; i < len1+1; i++ {
dp[i] = make([]int, len2+1)
}
for i := 1; i < len1+1; i++ {
dp[i][0] = i // 因为将前 i 个字符的 dna1 转换为空字符串需要 i 次删除操作
}
for j := 1; j < len2+1; j++ {
dp[0][j] = j // 因为将空字符串转换为前 j 个字符的 dna2 需要 j 次插入操作
}
dp[0][0] = 0 // 因为将空字符串转换为空字符串不需要任何操作
for i := 1; i < len1+1; i++ {
for j := 1; j < len2+1; j++ {
if dna1[i-1] == dna2[j-1] {
dp[i][j] = dp[i-1][j-1] // 如果 dna1[i-1] == dna2[j-1],则 dp[i][j] = dp[i-1][j-1],因为最后一个字符相同,不需要额外操作
} else {
dp[i][j] = min(dp[i-1][j]+1 /*删除操作*/, dp[i][j-1]+1/*插入操作*/, dp[i-1][j-1]+1 /*替换操作*/)
}
}
}
return dp[len1][len2]
}
func main() {
// You can add more test cases here
fmt.Println(solution("AGCTTAGC", "AGCTAGCT") == 2)
fmt.Println(solution("AGCCGAGC", "GCTAGCT") == 4)
}