动态规划
最长子序列问题
假定给出两组字符,求出两组字符的最长子序列长度,以及所有的最长子序列
子序列
子序列是指一个序列中的元素按照一定顺序排列而得到的新序列,这些元素不一定相邻。
比如,序列 [1, 3, 5] 的子序列有 [1, 5]、[3, 5]、[1, 3, 5] 等,其中 [1, 3, 5] 是它的最长子序列。
需要注意的是,子序列与子集的概念是不同的,子集是指序列中任意个数的元素组成的集合,包括空集和原序列,而子序列必须按顺序选取若干元素才能得到。
题目
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
-
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
步骤
使用字符数组表示两个字符串,下标从 1 ,而不是 0 开始
令 x = “tabcde”,y = “tace” (t用于占位使字符后移,可随意取值,后续不会使用该位置的值)
-
使用二维数组dp[len1 + 1][len2 + 1]记录子字符串的最长子序列情况
- len1、len2为两个初始子序列(不是拼接后的x、y字符串)的长度
- dp[i][j]表示第一个字符串前 i 个字符与第二个字符串前 j 个字符的最长子序列长度
-
填充二维数组 dp[i][k]
- 第一行、第一列的值都为0,即i == 0 或 k == 0 时 dp[i][k] = 0
- 因为另一个字符串的子序列没有字符,自然不能有子序列
- 之后 dp[i][j]
- 若x[i] == y[k],dp[i][k] = d[i - 1] + dp[k - 1] + 1
- 左上方数值加一,选取了该位置的字符
- 若x[i] != y[k],dp[i][k] = max{dp[i - 1][k],dp[i][k - 1]}
- 不要该位置的字符,从已知邻居(左边、上边)选一个最大的
- 若x[i] == y[k],dp[i][k] = d[i - 1] + dp[k - 1] + 1
- 第一行、第一列的值都为0,即i == 0 或 k == 0 时 dp[i][k] = 0
-
dp[len1][len2]的值即为最长子序列长度
填充示例
x/y | 0 | 1a | 2b | 3c | 4d | 5e |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1a | 0 | 1 | 1 | 1 | 1 | 1 |
2c | 0 | 1 | 1 | 2 | 2 | 2 |
3e | 0 | 1 | 1 | 2 | 2 | 3 |
- 首行首列全为0
- dp[2][2]
- x[2] = ‘c’ y[2] = ‘b’,不相等。取最大的邻居 --> 1
- dp[2][3]
- x[2] = ‘c’ y[3] = ‘c’,相等。 左上方 1 + 1 = 1
- dp[2][4]
- 不相等,邻居取最大,左邻居 2
代码
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
string x = "t" + text1;
string y = "t" + text2;
int dp[x.size()][y.size()];
// 首行、首列为0
for(int i = 0; i < x.size(); i++)
dp[i][0] = 0;
for(int i = 0; i < y.size(); i++)
dp[0][i] = 0;
// 行遍历填充二维数组
for(int i = 1; i < x.size(); i++) { // 列填充x
for(int k = 1; k < y.size(); k++) { // 行填充y
dp[i][k] = dp[i][k - 1]; // 默认:左邻居更大
if(x[i] == y[k]) // 相等左上方加一
dp[i][k] = dp[i - 1][k - 1] + 1;
else if(dp[i - 1][k] > dp[i][k - 1]) // 实际:上邻居更大
dp[i][k] = dp[i - 1][k];
}
}
return dp[x.size() - 1][y.size() - 1];
}
};