剑指 Offer II 095. 最长公共子序列-动态规划

剑指 Offer II 095. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

动态规划

两个字符串可能存在多个公共子序列,题目要求计算最长公共子序列的长度,因此可以考虑使用动态规划来解决。

二维DP数组

  • 函数 f ( i , j ) f(i,j) f(i,j)​ : 返回字符串s1[0…i]和字符串s2[0…j]的最长公共子序列的长度
  • 对于函数 f ( i , j ) f(i,j) f(i,j),如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j],所以 f ( i , j ) = f ( i − 1 , j − 1 ) + 1 f(i,j)=f(i-1,j-1)+1 f(i,j)=f(i1,j1)+1
  • 对于函数 f ( i , j ) f(i,j) f(i,j),如果 s [ i ] ! = s [ j ] s[i]!=s[j] s[i]!=s[j],所以 f ( i , j ) = M A X ( f ( i − 1 , j ) , f ( i , j − 1 ) ) f(i,j)=MAX( f(i-1,j), f(i,j-1) ) f(i,j)=MAX(f(i1,j),f(i,j1))

用函数 f(i, j) 表示字符串 s1 中下标从 0 开始到 i 的子字符串 s1[0…i] 和字符串 s2 中下标从 0 开始到 j 的子字符串 s2[0…j] 的最长公共子序列的长度。对于 f(i, j),如果 s1[i] == s2[j],那么相当于在 s1[0…i - 1] 和 s2[0…j - 1] 的最长公共子序列的后面添加一个公共字符,也就是 f(i, j) = f(i - 1, j - 1) + 1。如果 s1[i] != s2[j],那么这两个字符不可能出现在 s1[0…i] 和 s2[0…j] 的公共子序列中。此时 s1[0…i] 和 s2[0…j] 的最长公共子序列是s1[0…i - 1] 和 s2[0…j] 的最长公共子序列和s1[0…i] 和 s2[0…j - 1] 的最长公共子序列中的较大值,即 f(i, j) = max(f(i - 1, j), f(i, j - 1))。所以转移状态方程为

剑指 Offer II 095. 最长公共子序列-动态规划_第1张图片

因为状态方程有两个变量,所以需要使用二维矩阵保存。同时上述方程会出现 i 或者 j 出现 -1 的情况,代表出现 -1 下标的字符串的子串目前是空的,那么就不会有公共子序列,所以 f(i, -1) = f(-1, j) = 0。以 “abcde” 和 “badfe” 为例子,二维状态矩阵如下图

剑指 Offer II 095. 最长公共子序列-动态规划_第2张图片

实现

package 力扣;

/**
 * @author yyq
 * @create 2022-03-01 10:22
 * 剑指 Offer II 095. 最长公共子序列
 * 解题思路:采取动态规划,利用空间换时间
 *
 */
public class offerII095LongSequence {
    public static void main(String[] args) {
        System.out.println(longestCommonSubsequence("abcde", "ace"));
        System.out.println(longestCommonSubsequence("abc", "abc"));
        System.out.println(longestCommonSubsequence("abc", "def"));

    }
    public static int longestCommonSubsequence(String text1, String text2) {
        //  创建缓存数组,maxlength[1][2]代表text1[0-1]和text2[0-2]中的最长公共子序列长度
        int[][] maxlenth=new int[text1.length()+1][text2.length()+1];
        // 第一行和第一列为空
        for (int i=0;i<maxlenth[0].length;i++)
            maxlenth[0][i]=0;
        for (int i=0;i<maxlenth.length;i++)
            maxlenth[i][0]=0;
        //  循环遍历缓存数组,生成数组的每一个数值
        for (int i = 1; i < maxlenth.length; i++) {
            for (int j = 1; j < maxlenth[0].length; j++) {
                int a=i-1;
                int b=j-1;
                // 情况一 如果当前的位置对应的字符相同
                if(text1.charAt(a)==text2.charAt(b)){
                    maxlenth[i][j]=maxlenth[i-1][j-1]+1;
                }
                // 情况二 对应的字符不相同
                else {
                    int max=maxlenth[i-1][j];
                    if(maxlenth[i][j-1]>max){
                        max=maxlenth[i][j-1];
                    }
                    maxlenth[i][j]=max;
                }
            }
        }
        return maxlenth[text1.length()][text2.length()];
    }
}

你可能感兴趣的:(LeetCode,动态规划,leetcode,算法)