给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,
"ace"
是"abcde"
的子序列,但"aec"
不是"abcde"
的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace" 输出:3 解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc" 输出:3 解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:
输入:text1 = "abc", text2 = "def" 输出:0 解释:两个字符串没有公共子序列,返回 0 。
提示:
1 <= text1.length, text2.length <= 1000
text1
和text2
仅由小写英文字符组成。
解题思路
-
定义状态:
- 定义二维数组
dp
,其中dp[i][j]
表示text1
的前i
个字符与text2
的前j
个字符的最长公共子序列的长度。
- 定义二维数组
-
初始化:
- 初始化
dp[0][0] = 0
,表示两个空字符串的最长公共子序列长度为0。
- 初始化
-
状态转移:
- 遍历
text1
和text2
的每个字符,如果text1.charAt(i-1) == text2.charAt(j-1)
,则dp[i][j] = dp[i-1][j-1] + 1
,因为它们属于最长公共子序列的一部分。 - 如果
text1.charAt(i-1) != text2.charAt(j-1)
,则dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])
,选择不同字符时的最长公共子序列长度。
- 遍历
-
结果:
- 最终
dp[n][m]
即为text1
和text2
的最长公共子序列的长度。
- 最终
示例
假设 text1 = "abcde"
,text2 = "ace"
,我们将填充 dp
表格以找到最长公共子序列的长度:
dp[0][0]
初始化为0
。- 遍历
text1
和text2
的每个字符:- 当
i=1, j=1
时,text1.charAt(0)='a'
与text2.charAt(0)='a'
相同,所以dp[1][1] = dp[0][0] + 1 = 1
。 - 当
i=2, j=2
时,text1.charAt(1)='b'
与text2.charAt(1)='c'
不同,所以dp[2][2] = Math.max(dp[1][2], dp[2][1]) = 1
。 - 以此类推,直到填满整个
dp
表格。
- 当
最终 dp[5][3]
的值为 3
,这就是 "abcde"
和 "ace"
的最长公共子序列的长度。
public int longestCommonSubsequence(String text1, String text2) {
// 获取字符串text1的长度
int n = text1.length();
// 获取字符串text2的长度
int m = text2.length();
// 创建一个二维数组dp,dp[i][j]表示text1的前i个字符与text2的前j个字符的最长公共子序列的长度
int[][] dp = new int[n + 1][m + 1];
// 初始化dp数组
dp[0][0] = 0;
// 遍历text1和text2的每个字符,填充dp数组
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 如果text1的第i个字符与text2的第j个字符相同
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
// 则dp[i][j]为dp[i-1][j-1]加上1,因为这两个字符属于最长公共子序列的一部分
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
// 否则,取dp[i-1][j]和dp[i][j-1]的较大值,表示不同字符时的最长公共子序列长度
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// 返回text1和text2的最长公共子序列的长度,即dp[n][m]
return dp[n][m];
}