题目
给定两个字符串str1和str2,输出连个字符串的最长公共子序列。如过最长公共子序列为空,则输出-1。
输入描述:
输出包括两行,第一行代表字符串str1,第二行代表str2。\left( 1\leq length(str1),length(str2) \leq 5000\right)(1≤length(str1),length(str2)≤5000)
输出描述:
输出一行,代表他们最长公共子序列。如果公共子序列的长度为空,则输出-1。
示例1
输入
1A2C3D4B56
B1D23CA45B6A
输出
123456
说明
"123456"和“12C4B6”都是最长公共子序列,任意输出一个。
函数说明
此次方法选择动态规划的方法;
用代码和以下两张图对照看,可以更加清楚:
以下两张图来源于:(关于思路可以参考以下链接文章,代码可以参考本文)
https://blog.youkuaiyun.com/ggdhs/article/details/90713154?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1
如果有两个字符串如下:
S1 = “123456778”
S2 = “357486782”
其最终的动态规划填表结果为:
/***************************************************
统计公共子串长度,并将完成标签数组的记录。
s1,s2输入的字符串,len记录最长公共子串的长度
tag标签数组指向一个二维数组,标记字符串中哪个字符被选做为最长公共子序列中的一个
****************************************************/
void LCS(string s1, string s2,int &len,int **tag);
/***********************************************
找到公共子序列,并记录在res中
利用标签数组tag的记录来输出公共子序列
tag中:0代表选择这个元素,1代表可能选择同列前一行的元素,- 1代表可能选择同行前一列的元素
s1输入的字符串(s2也行);
i,j代表寻找到标签数组的行列号;
res代表输出的公共子序列
************************************************/
void Print(string s1, int i, int j, string& res, int** tag);
代码
#include<iostream>
#include<string>
#include<vector>
using namespace std;
/***************************************************
统计公共子串长度,并将完成标签数组的记录。
s1,s2输入的字符串,len记录最长公共子串的长度
tag标签数组指向一个二维数组,标记字符串中哪个字符被选做为最长公共子序列中的一个
****************************************************/
void LCS(string s1, string s2,int &len,int **tag);
/***********************************************
找到公共子序列,并记录在res中
利用标签数组tag的记录来输出公共子序列
tag中:0代表选择这个元素,1代表可能选择同列前一行的元素,- 1代表可能选择同行前一列的元素
s1输入的字符串(s2也行);
i,j代表寻找到标签数组的行列号;
res代表输出的公共子序列
************************************************/
void Print(string s1, int i, int j, string& res, int** tag);
void main() {
int i = 0, j = 0;
string s1 , s2;
int len=0;//最长公共子序列长度
string res;//记录最长公共子序列
//cin >> s1 >> s2;
s1 = "1A2C3D4B56";
s2 = "B1D23CA45B6A";
//对标签数组开辟空间。
int** tag = new int*[s1.size() + 1];//指向一个二维数组,标记字符串中哪个字符被选做为最长公共子序列中的一个
for(int i=0;i<s1.size()+1;++i)
tag[i] =new int[s2.size()+1];
memset(tag, 0, sizeof(tag));//初始化0
LCS(s1, s2, len,tag);
Print( s1,s1.size(), s2.size(),res,tag);
cout <<len<<" "<< res << endl;
//对标签数组进行释放内存
for (int i = 0; i < s1.size() + 1; ++i)
delete[]tag[i];
delete[]tag;
}
void LCS(string s1, string s2, int& len, int **tag) {
int m = s1.size();
int n = s2.size();
vector<vector<int>>tmp(m+1, vector<int>(n+1, 0));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s1[i - 1] == s2[j - 1]) {
tmp[i][j] = tmp[i - 1][j - 1] + 1;
tag[i][j] = 0;
}
else {
if (tmp[i - 1][j] >= tmp[i][j - 1])
tmp[i][j] = tmp[i - 1][j], tag[i][j] = 1;
else
tmp[i][j] = tmp[i][j - 1], tag[i][j] = -1;
}
}
}
len = tmp[m][n];
}
//tag 0代表选择这个元素,1代表选择同列前一行的元素,-1代表选择同行前一列的元素
void Print(string s1,int i,int j, string& res, int **tag) {
if (i == 0 || j == 0)
return;
if (tag[i][j] == 0) {
//res += s1[i - 1];//这样是顺着输出
Print( s1,i - 1, j - 1, res,tag);
res += s1[i - 1];//这样是倒着输出,递归时从后往前输出,倒倒得顺
}
else if (tag[i][j] == 1)
Print(s1, i - 1, j ,res,tag);
else
Print(s1, i , j - 1,res,tag);
}
当对tag不想用二级指针,用vector时代码如下:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
/***************************************************
统计公共子串长度,并将完成标签数组的记录。
s1,s2输入的字符串,len记录最长公共子串的长度
tag标签数组指向一个二维数组,标记字符串中哪个字符被选做为最长公共子序列中的一个
****************************************************/
void LCS(string s1, string s2, int& len, vector<vector<int>>& tag);
/***********************************************
找到公共子序列,并记录在res中
利用标签数组tag的记录来输出公共子序列
tag中:0代表选择这个元素,1代表可能选择同列前一行的元素,- 1代表可能选择同行前一列的元素
s1输入的字符串(s2也行);
i,j代表寻找到标签数组的行列号;
res代表输出的公共子序列
************************************************/
void Print(string s1, int i, int j, string& res, vector<vector<int>>& tag);
void main() {
int i = 0, j = 0;
string s1, s2;
int len = 0;//最长公共子序列长度
string res;//记录最长公共子序列
//cin >> s1 >> s2;
s1 = "1A2C3D4B56";
s2 = "B1D23CA45B6A";
vector<vector<int>>tag(s1.size() + 1, vector<int>(s2.size() + 1, 0));
LCS(s1, s2, len, tag);
Print(s1, s1.size(), s2.size(), res, tag);
cout << len << " " << res << endl;
}
void LCS(string s1, string s2, int& len, vector<vector<int>>& tag) {
int m = s1.size();
int n = s2.size();
vector<vector<int>>tmp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (s1[i - 1] == s2[j - 1]) {
tmp[i][j] = tmp[i - 1][j - 1] + 1;
tag[i][j] = 0;
}
else {
if (tmp[i - 1][j] >= tmp[i][j - 1])
tmp[i][j] = tmp[i - 1][j], tag[i][j] = 1;
else
tmp[i][j] = tmp[i][j - 1], tag[i][j] = -1;
}
}
}
len = tmp[m][n];
}
//tag 0代表选择这个元素,1代表选择同列前一行的元素,-1代表选择同行前一列的元素
void Print(string s1, int i, int j, string& res, vector<vector<int>>& tag) {
if (i == 0 || j == 0)
return;
if (tag[i][j] == 0) {
//res += s1[i - 1];//这样是顺着输出
Print(s1, i - 1, j - 1, res, tag);
res += s1[i - 1];//这样是倒着输出,递归时从后往前输出,倒倒得顺
}
else if (tag[i][j] == 1)
Print(s1, i - 1, j, res, tag);
else
Print(s1, i, j - 1, res, tag);
}