动态规划求解子序列问题 和 编辑距离

动态规划求解子序列问题

思路

这类题基本就三步:

  1. 确定动态数组含义
  2. 写出转移方程
  3. 给出basecase(基础解)

LeetCode 1143

image-20220412210001473

  1. 确定动态数组含义:dp[i] [j]为text1前i个字符 和 text2前j个字符的 lcs(最长公共子序列) 长度
  2. 转移方程:
    • 当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长度一定不大于前两种,可以直接忽略)
      • 取三种情况的最大值即可
  3. 确定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

image-20220412212118965

  1. 确定动态数组含义:dp[i] [j]为使得word1前i个字符 和 word2前j个字符相同所需的最小步数
  2. 转移方程:
    • 当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])
      • 取三种情况的最小值即可
  3. 确定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

image-20220412213332082

这题与上一题相似,直接给代码

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

image-20220412213636523

  1. 确定动态数组含义:dp[i] [j]为word1前i个字符 和 word2前j个字符的编辑距离
  2. 转移方程:
    • 当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]中的一个)
      • 取三种情况的最小值即可
      • 删除一个字符等价于为另一个字符串插入一个字符,顾都讨论删除
  3. 确定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];
    }
}
posted @ 2022-04-12 21:45  codeSpiderMan  阅读(57)  评论(0)    收藏  举报