最長公共子序列長度和輸出(史上註釋最全,最完整動態規劃方法的代碼)(二級指針用法,在調用前開闢空間)

題目

給定兩個字符串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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章