LeetCode 1143.最长公共子序列

※ 最长公共子序列

对于两个子序列 S1 和 S2,找出它们最长的公共子序列。

定义一个二维数组 dp 用来存储最长公共子序列的长度,其中$ dp[i][j] $表示 S1 的前 i 个字符与 S2 的前 j 个字符最长公共子序列的长度。考虑 $S1_i $与$ S2_j $值是否相等,分为两种情况:

  • 当 $S1_i==S2_j $时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上$ S1_i $这个值,最长公共子序列长度加 1,即 $dp[i][j] = dp[i-1][j-1] + 1$。
  • 当 $S1_i != S2_j $时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,或者 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,取它们的最大者,即 $dp[i][j] = max({ dp[i-1][j], dp[i][j-1] })$。

综上,最长公共子序列的状态转移方程为:

对于长度为 N 的序列 S1 和长度为 M 的序列 S2,$dp[N][M] $就是序列 S1 和序列 S2 的最长公共子序列长度。

与最长递增子序列相比,最长公共子序列有以下不同点:

  • 针对的是两个序列,求它们的最长公共子序列。
  • 在最长递增子序列中,$dp[i]$ 表示以 $S_i $为结尾的最长递增子序列长度,子序列必须包含 $S_i$ ;在最长公共子序列中,$dp[i][j] $表示 S1 中前 i 个字符与 S2 中前 j 个字符的最长公共子序列长度,不一定包含 $S1_i $和 $S2_j$。
  • 在求最终解时,最长公共子序列中 $dp[N][M] $就是最终解,而最长递增子序列中 $dp[N] $不是最终解,因为以 $S_N$ 为结尾的最长递增子序列不一定是整个序列最长递增子序列,需要遍历一遍 dp 数组找到最大者。

1143. 最长公共子序列

1143.Longest Common Subsequence

Leetcode / 力扣

题目描述

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

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,”ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

1
2
3
4
5
示例 1:

输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",它的长度为 3。

解题思路

同上,记住状态转移方程就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int n1 = text1.length(), n2 = text2.length();
int[][] dp = new int[n1 + 1][n2 + 1];

for(int i = 1; i <= n1; i++){
for(int j = 1; j <= n2; 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[n1][n2];
}
}