经典dp--字符串系列之Go写法
-
- [Edit Distance](https://leetcode.com/problems/edit-distance/)
- [Longest Common Subsequence](https://leetcode.com/problems/longest-common-subsequence/)
- [剑指 Offer 19. 正则表达式匹配](https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/)
- [44. 通配符匹配](https://leetcode-cn.com/problems/wildcard-matching/)
- [32. 最长有效括号](https://leetcode-cn.com/problems/longest-valid-parentheses/)
在这里我目前碰到了一些的字符串dp处理的问题,这些问题结构很相似,都是借助二维数组来保存状态,根据这种状态来转移更新。其中1、2是一组题,3、4是一组题
PS:画个矩阵出来比一比更加形象生动!!!
Edit Distance
编辑距离这道题连接和题目描述见原链接(标题)
首先,我们定义一个dp
数组,大小为m = len(word1), n = len(word2)
。dp[i][j]的含义表示子串word1[0:i]
和word2[0:j]
的最小编辑距离。
然后,我们可知操作主要有四种,也就是插入、删除、替换和什么都不搞,这四种操作我们可以看做获得最优解的一个状态,也就是说我们当前状态是通过这四种操作而获得。
所以我们有情况,当word1[i] == word2[j]
时,dp[i][j]= dp[i-1][j-1]
, 因为两个相等了,就不需要操作了;当word1[i] != word2[j]
时, 这是我们有三种操作可以选择,当然,我们求最小编辑距离,就是从三种操作中选择最小的那个:min(dp[i-1][j], dp[i][j-1],dp[i-1][j-1]) + 1
在这里特别说明一下dp[i][j-1]
表示对word1
进行插入,dp[i-1][j]
表示对word1
进行删除,dp[i-1][j-1]
表示对word1
进行替换
最后,我们需要明确一下边界,也就是初始值,我们需要对dp
数组进行初始化,也就是dp
数组大小是(m+1) x (n+1)
的,初始化的值为对应的长度。举个简单的例子就是,假定word2
长度为0
,对于子串word1[0:i]
对应的初始值dp[0][i] = i
, 因为这个匹配是要不断删除word1
字母,同理dp[i][0] = i
func minDistance(word1 string, word2 string) int {
len1 := len(word1)
len2 := len(word2)
dp := make([][]int, len1+1)
for i:=0; i<=len1; i++ {
dp[i] = make([]int, len2+1)
}
// init 边界
for i:=1; i<=len2; i++ {
dp[0][i] = i
}
for i:=1; i<=len1; i++ {
dp[i][0] = i
}
// finish init
for i:=1; i<=len1; i++ {
for j:=1; j<=len2;j++ {
// 根据状态转移方程计算dp[i][j]
if word1[i-1] == word2[j-1] {
dp[i][j] = dp[i-1][j-1]
} else {
dp[i][j] = minValues(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1
}
}
}
return dp[len1][len2]
}
func minValues(values ...int) int {
res := math.MaxInt32
for _, v := range values {
if v < res {
res = v
}
}
return res
}
Longest Common Subsequence
有了上面那个例子的,我们这个题也是相对比较类似的
首先,我们定义一个dp
数组,大小为m = len(text1), n = len(text2)
。dp[i][j]
表示子串text1[0:i]
和text2[0:j]
的之间最大的公共子序列(LCS)。
然后我们需要确定状态,在这里状态有三种,当text1[i] == text2[j]
时,我们的LCS比dp[i-1][j-1]
要长一个单位;当text1[i] != text2[j]
时, 我们可以根据dp[i-1][j]
或者 dp[i][j-1]
这两个状态中选一个最大的作为我们的值:
**边界问题**则相对来说比较简单,某个`text`为空,其`dp`数组都为`0`,因为没有LCS。
func longestCommonSubsequence(text1 string, text2 string) int {
len1 := len(text1)
len2 := len(text2)
dp := make([][]int, len1 + 1)
for i:=0; i<=len1; i++ {
dp[i] = make([]int, len2+1)
} //finish init
for i:=1; i<=len1; i++ {
for j :=1; j<=len2;j+&