Description
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0。
一个字符串的 子序列是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
点击这里查看原题
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
Analysis
注意题目要求的是公共子序列,与字串需要加以区分:
子串必须是连续的,而子序列不一定连续。
如:字符串 " abd " 与 " abcd " 的最长公共字符串为 " ab ", 最长公共子序列为 “abd”。
弄清楚了什么是子序列,我们开始分析:
还是上边的例子:求字符串 str1 = " abd " 与 "str2 = abcd " 的最长子序列。
我们从后往前注意比较两个字符串:
str1[2] == str2[3],两字符相等,那么最终的答案中一定包含该字符,继续往前比较, str1[1] != str2[2],我们需要再次寻找相同的字符,这时可以有两种方式:str1指针往前移 or str2指针前移。求最值问题可以使用动态规划方法,根据上边的分析:
- dp[i][j]表示str1长度为i、str2长度为j时最长公共序列长度,因而需要创建一个str1长度加1 行、str2长度加1 列的二维数组(保证了下标可以到达 str1.length() 以及 str2.length())
- 根据dp数组的含义:当str1[i] == str2[j] 时,dp[i][j] =dp[i - 1][j - 1] + 1; str1[i] != str2[j] 时, dp[i][j] = max(dp[i - 1][j], dp[i][j -1]);
- 由递推公式,遍历顺序为从左至右(列方向),从上往下(行方向);考虑到dp[i][j]可能会使用dp[i - 1][j] 以及 dp[i][j - 1],因此对于边界需要特殊处理,一种行之有效的方法是对dp数组行、列都扩充一行。在创建dp数组的时候已经保证了这一点。
对于字符串 " ABCBDAB " 、 " BDCABA ", 最后填充完毕的dp数组为
箭头表示该处的结果建立在之前的某结果上。(方便查看)
Code
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void subSequence(string& str1, string& str2, vector<vector<int>>& dp);
void backTrace(string& str1, string& str2, vector<vector<int>>& dp);
//求最长子序列长度
void subSequence(string& str1, string& str2,vector<vector<int>>& dp) {
for (int row = 1; row <= str1.length(); row++) {
for (int col = 1; col <= str2.length(); col++) {
if (str1[row - 1] == str2[col - 1])
dp[row][col] = dp[row - 1][col - 1] + 1;
else
dp[row][col] = max(dp[row - 1][col], dp[row][col - 1]);
}
}
backTrace(str1, str2, dp);
}
//回溯查找出最长子序列
void backTrace(string& str1, string& str2, vector<vector<int>>& dp) {
string res = "";
int curRow = dp.size() - 1, curCol = dp[0].size() - 1;
while (curRow > 0 && curCol > 0) {
if (str1[curRow - 1] == str2[curCol - 1]) {
res.insert(res.begin(), str1[curRow - 1]);
curRow--, curCol--;
}
else {
if (dp[curRow - 1][curCol] >= dp[curRow][curCol - 1]) {
curRow--;
}
else curCol--;
}
}
cout << "最长公共子序列为: "<< res;
}
int main() {
string str1, str2;
cin >> str1 >> str2;
//n + 1行 m + 1列
//dp[i][j] 表示str1长度为 i、str2长度为j 时的最长公共子序列长度
vector<vector<int>> dp(str1.length() + 1, vector<int>(str2.length() + 1, 0));
subSequence(str1, str2, dp);
return 0;
}
Running
相似题
在阅读完本文之后,可以尝试以下类似题:
巧妙利用LCS求解 ---->>>>> 583. 两个字符串的删除操作
LCS系列经典面试题 ---->>>>> 1035. 不相交的线
变体 ----->>>>> 最长重复子数组