思路来自邓俊辉—数据结构(C++)
2019/08/23
递归思路
对于序列A[0,n] 和B[0,m], LCS(A, B)有三种情况:
0)若n = -1或m = -1,则取作空序列(" ");
1)若A[n] == ‘X’ == B[m],则取作LCS( A[0, n) , B[0, m) + ‘X’;
2)若A[n] != B[m],则在LCS( A[0, n], B[0, m) ) 与LCS( A[0, n), B[0, m] ) 中取更长者;
代码1.0(返回重复个数)
int LCS(const string& a, const string& b){ //返回公共子序列的字符个数
int n = a.length();
int m = b.length();
if(n == 0 || m ==0) //情况1
return 0;
else if (a.back() == b.back()) //情况2
return LCS(a.substr(0, n - 1), b.substr(0, m-1)) + 1;
else //情况3
return max(LCS(a.substr(0, n), b.substr(0, m - 1)), //取两者最大值
LCS(a.substr(0, n - 1), b.substr(0, m)));
}
2019/08/24
代码1.1(返回重复字符串)
string max(string A, string B){ //返回更长的字符串
return A.length() > B.length() ? A : B;
}
string LCS(string A, string B){
int len_A = A.length();
int len_B = B.length();
if( len_A == 0 || len_B == 0) //情况1
return "";
else if (A.back() == B.back()) //情况2
return LCS(A.substr(0, len_A - 1), B.substr(0, len_B - 1)) + A.back();
else //情况3
return max(LCS(A.substr(0, len_A), B.substr(0, len_B - 1)),
LCS(A.substr(0, len_A - 1), B.substr(0, len_B)));
}
动态规划思路
1.为什么使用动态规划:采用动态规划时,并不需要去一 一 计算那些重叠了的子问题。或者说:用了动态规划之后,有些子问题 是通过 “查表“ 直接得到的,而不是重新又计算一遍得到的。(类似Fib数列用递归方法计算时会重复计算部分项)
2.如何得到dp数组:
设序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则:
1.若xm=yn,则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列;
2.若xm≠yn且zk≠xm ,则Z是Xm-1和Y的最长公共子序列;
3.若xm≠yn且zk≠yn ,则Z是X和Yn-1的最长公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
简而言之一张图(注意图有一处错误:i=0 或 j = 0时,C[i, j]不一定是0):
3.如何利用得到dp二维数组得到公共子序列:若两序列A[i]的元素和B[i]的元素相等,那么最长公共子序列为A[0, i -1], B[0, j - 1]的最长公共子序列的值加1。否则分别是A[0, i- 1], B[0 ,j] 或者A [0, i - 1], B[0, j ]的最长公共子序列的较大值。
代码2.0(动态规划)
int** LCS(const string a, const string b){
int la = a.length();
int lb = b.length();
//创建动态二维数组
int **dp = new int *[la];
for(int i = 0; i < la; ++i)
dp[i] = new int[lb];
//设置左、上边界的值
for(int i = 0; i < lb; ++i) //上
if(a[0] == b[i])
dp[0][i] = 1;
else dp[0][i] = 0;
for(int i = 0; i < la; ++i) //左
if(b[0] == a[i])
dp[i][0] = 1;
else dp[i][0] = 0;
int maxium = 0; //记录最大值
for(int i = 1; i < la; ++i) //得到字符串a、b第i、j个字符
for(int j =1; j < lb; ++j){
if(a[i] == b[j])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i -1][j], dp[i][j - 1]);
if(dp[i][j] > maxium)
maxium = dp[i][j];
}
string result = ""; //保存公共子序列
int k = la, t = lb;
while (maxium >= 0) {
if (a[k] == b[t]) {
result += a[k];
--k; --t;
--maxium;
}
else {
if (dp[k][t - 1] >= dp[k - 1][t]) --t;
else --k;
}
}
reverse(result.begin(), result.end()); //头文件<algorithm>
cout << result << endl;
return dp;
//主函数一定要记得销毁二维动态数组所占的空间
}
tip :创建、销毁动态二维数组
//创建动态二维数组
int **dp = new int *[la];
for(int i = 0; i < la; ++i)
dp[i] = new int[lb];
//销毁动态二维数组
for(int i = 0; i < la; ++i){
delete[] dp[i];
dp[i] = NULL;
}
delete[] dp;
dp = NULL;