动态规划求解子序列问题 和 编辑距离
动态规划求解子序列问题
思路
这类题基本就三步:
- 确定动态数组含义
- 写出转移方程
- 给出basecase(基础解)
LeetCode 1143

- 确定动态数组含义:dp[i] [j]为text1前i个字符 和 text2前j个字符的 lcs(最长公共子序列) 长度
- 转移方程:
- 当text1(i)==text2(j)时,dp[i] [j]=dp[i-1] [j-1] +1
- 当text1(i)!=text2(j)时,有三种情况:
- text1[0...i-1] 和 text2[0...j-1] 的lcs长度等于text1[0...i-2] 和 text2[0...j-1]的lcs长度
- text1[0...i-1] 和 text2[0...j-1] 的lcs长度等于text1[0...i-1] 和 text2[0...j-2]的lcs长度
- text1[0...i-1] 和 text2[0...j-1] 的lcs长度等于text1[0...i-2] 和 text2[0...j-2]的lcs长度(这种情况下的lcs长度一定不大于前两种,可以直接忽略)
- 取三种情况的最大值即可
- 确定basecase
- 当其中一个字符串为零时,lcs长度=0
题解:
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m=text1.length(),n=text2.length();
if(m==0|| n==0) return 0;
int[][] dp=new int[m+1][n+1];
// 定义:text1[0..i-1] 和 text2[0..j-1] 的 lcs 长度为 dp[i][j]
// base case: dp[0][..] = dp[..][0] = 0
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(text1.charAt(i-1)==text2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1;
else {
dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
LeetCode 583

- 确定动态数组含义:dp[i] [j]为使得word1前i个字符 和 word2前j个字符相同所需的最小步数
- 转移方程:
- 当word1(i)==word2(j)时,dp[i] [j]=dp[i-1] [j-1]
- 当word1(i)!=word2(j)时,有三种情况:
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-2] 和 text2[0...j-1]相同所需的最小步数+1(即删除了word1[i-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-1] 和 text2[0...j-2]相同所需的最小步数+1(即删除了word2[j-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-2] 和 text2[0...j-2]相同所需的最小步数+1(即删除了word1[i-1]和word2[j-1])
- 取三种情况的最小值即可
- 确定basecase
- 当其中一个字符串为零时,相同所需的最小步数为另一个字符串的长度
题解:
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int[][] dp=new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 1; j <=n ; j++) {
dp[0][j] = j;
}
for (int i = 1; i <=m ; i++) {
for (int j = 1; j <=n ; j++) {
if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1];
else {
dp[i][j]=Math.min(Math.min(dp[i-1][j]+1,dp[i][j-1]+1), dp[i-1][j-1]+2);
}
}
}
return dp[m][n];
}
}
另一种思路,用上一题(1143)的结论,因为要使步数最小,也就是删除两个字符串除了最长公共子序列之外的所有元素即可。代码如下:
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int lcs = longestCommonSubsequence(word1,word2,m,n);
return m+n-2*lcs;
}
public int longestCommonSubsequence(String word1, String word2,int m,int n) {
if(m==0|| n==0) return 0;
int[][] dp=new int[m+1][n+1];
// 定义:s1[0..i-1] 和 s2[0..j-1] 的 lcs 长度为 dp[i][j]
// base case: dp[0][..] = dp[..][0] = 0
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]+1;
else {
dp[i][j]=Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}
LeetCode712

这题与上一题相似,直接给代码
class Solution {
public int minimumDeleteSum(String s1, String s2) {
int m=s1.length(),n=s2.length();
int[][] dp=new int[m+1][n+1];
for (int i = 1; i <= m; i++) {
dp[i][0] = dp[i-1][0]+s1.charAt(i-1);
}
for (int j = 1; j <=n ; j++) {
dp[0][j] = dp[0][j-1] + s2.charAt(j-1);
}
for (int i = 1; i <=m ; i++) {
for (int j = 1; j <=n ; j++) {
if(s1.charAt(i-1)==s2.charAt(j-1)) dp[i][j]=dp[i-1][j-1];
else {
dp[i][j]=Math.min(Math.min(dp[i-1][j]+s1.charAt(i-1),dp[i][j-1]+s2.charAt(j-1)), dp[i-1][j-1]+s1.charAt(i-1)+s2.charAt(j-1));
}
}
}
return dp[m][n];
}
}
动态规划 编辑距离 LeetCode72

- 确定动态数组含义:dp[i] [j]为word1前i个字符 和 word2前j个字符的编辑距离
- 转移方程:
- 当word1(i)==word2(j)时,dp[i] [j]=dp[i-1] [j-1]
- 当word1(i)!=word2(j)时,有三种情况:
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-2] 和 text2[0...j-1]相同所需的最小步数+1(即删除了word1[i-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-1] 和 text2[0...j-2]相同所需的最小步数+1(即删除了word2[j-1])
- word1[0...i-1] 和 word2[0...j-1] 相同所需的最小步数等于word1[0...i-2] 和 text2[0...j-2]相同所需的最小步数+1(即替换了word1[i-1]和word2[j-1]中的一个)
- 取三种情况的最小值即可
- 删除一个字符等价于为另一个字符串插入一个字符,顾都讨论删除
- 确定basecase
- 当其中一个字符串为零时,相同所需的最小步数为另一个字符串的长度
题解
class Solution {
public int minDistance(String word1, String word2) {
int m=word1.length(),n=word2.length();
int[][] dp =new int[m+1][n+1];
//s1[0..i-1] 和 s2[0..j-1] 的编辑距离表示为dp[i][j]
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int i = 0; i <= n; i++) {
dp[0][i]= i;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1];
else dp[i][j]=Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1])+1;
}
}
return dp[m][n];
}
}

浙公网安备 33010602011771号