-
583两个字符串的删除操作
- 代码随想录 (programmercarl.com)
-
第一印象
- 求删除操作的步骤数目,也就相当于求相同的最长子序列长度,然后再用两个word的总长和去减。因为如果相同的子序列如果短一位,那么相当于删除步骤就要增加2步。
-
讲解观后感
- 除了使用求得最长公共子序列的方法,我们也可已使用类似115不同子序列中讨论删除情况的方法来解题。
- 我们按照讨论删除的方法捋顺一遍。
- 先确定dp数组(dp table)以及下标的含义
dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
- 然后确定递推公式。分相等和不相等两种情况
- 当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i][j] = dp[i - 1][j - 1];
- 当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:
情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
取最小dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1})
- 这里我们可以通过意义得来,
dp[i-1][j-1]+2的情况一定是某一个[x-1]通过+1得到的。
所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)
- dp的初始化
- dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。dp[0][j]的话同理
- 确定遍历的顺序,通过观察状态转移公式。遍历的时候一定是从上到下,从左到右。
-
解题代码
- 动态规划删除数
-
func minDistance(word1 string, word2 string) int {
dp := make([][]int, len(word1)+1)
for i := 0; i < len(dp); i++ {
dp[i] = make([]int, len(word2)+1)
}
//初始化
for i := 0; i < len(dp); i++ {
dp[i][0] = i
}
for j := 0; j < len(dp[0]); j++ {
dp[0][j] = j
}
for i := 1; i < len(dp); i++ {
for j := 1; j < len(dp[i]); j++ {
if word1[i-1] == word2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else {
dp[i][j] = min(min(dp[i-1][j]+1, dp[i][j-1]+1), dp[i-1][j-1]+2)
}
}
}
return dp[len(dp)-1][len(dp[0])-1]
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
- 动态规划保留数
-
func minDistance(text1 string, text2 string) int {
t1 := len(text1)
t2 := len(text2)
dp:=make([][]int,t1+1)
for i:=range dp{
dp[i]=make([]int,t2+1)
}
for i := 1; i <= t1; i++ {
for j := 1; j <=t2; j++ {
if text1[i-1]==text2[j-1]{
dp[i][j]=dp[i-1][j-1]+1
}else{
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
}
}
}
return t1 + t2 - dp[t1][t2] * 2 //分别在两个字符串中减去重复的子序列
}
func max(a,b int)int {
if a>b{
return a
}
return b
}
-
72编辑距离
- 代码随想录 (programmercarl.com)
-
第一印象
- 结合之前的题目经历。我可以比较方便的想到当匹配到的字符在相同时和不同时,使用删除时的情况。但是在考虑删除或是增加的操作时,我们还要已匹配的长度关系。但是当我们同时使用删除和增加操作时,其实和只使用一种操作在另一个字符串的意义是一样的。
- 以力扣题目示例来讨论
-
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
来源:力扣(LeetCode)
链接:https:
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 我们在'exection'插入‘u’的时候,其实是和在‘execution’删除‘u’是一样的。
- 所以我们可以利用类似于上面583的方法。
-
讲解观后感
- 还是按照动规的固定思考顺序
- 先确定dp数组(dp table)以及下标的含义。dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。这样做是为了方便符合意义的初始化。
- 然后来确定递推公式。首先要考虑清楚编辑的几种操作
-
if (word1[i - 1] == word2[j - 1])
不操作
if (word1[i - 1] != word2[j - 1])
增
删
换
if (word1[i - 1] == word2[j - 1]) 那么说明不用任何编辑, dp[i][j] 就应该是 dp[i - 1][j - 1] ,即 dp[i][j] = dp[i - 1][j - 1]
if (word1[i - 1] != word2[j - 1]) ,此时就需要编辑了:
- 操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离再加上一个操作。
即 dp[i][j] = dp[i - 1][j] + 1
- 操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离再加上一个操作。(这里在word2删除一个元素就相当于在word1增加一个元素的操作)
即 dp[i][j] = dp[i][j - 1] + 1;
- 操作三:替换元素,
word1 替换 word1[i - 1] ,使其与 word2[j - 1] 相同,此时进行更改操作。只需要一次替换的操作,就可以让 word1[i - 1] 和 word2[j - 1] 相同。所以 dp[i][j] = dp[i - 1][j - 1] + 1
- 综上,当
if (word1[i - 1] != word2[j - 1]) 时取最小的,即: dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1
- 因此递推公式为
-
if word1[i - 1] == word2[j - 1] {
dp[i][j] = dp[i - 1][j - 1]
} else {
dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1
}
- 接下来初始化,dp[i][0] 和 dp[0][j] 表示什么呢?
- dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。
- 那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i。同理dp[0][j] = j。
-
解题代码
-
func minDistance(word1 string, word2 string) int {
m, n := len(word1), len(word2)
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for i := 0; i < m+1; i++ {
dp[i][0] = i // word1[i] 变成 word2[0], 删掉 word1[i], 需要 i 部操作
}
for j := 0; j < n+1; j++ {
dp[0][j] = j // word1[0] 变成 word2[j], 插入 word1[j],需要 j 部操作
}
for i := 1; i < m+1; i++ {
for j := 1; j < n+1; j++ {
if word1[i-1] == word2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else { // Min(插入,删除,替换)
dp[i][j] = Min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1
}
}
}
return dp[m][n]
}
func Min(args ...int) int {
min := args[0]
for _, item := range args {
if item < min {
min = item
}
}
return min
}