題目
給定兩個字符串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.csdn.net/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);
}