目录
有两个字符串
str1:helloworld X:X1,X2,....Xn
str2:hlweord Y:Y1,Y2,....Ym
LCS最长公共子序列的长度
如果Xn == Ym
LCS(X[1....n],Y[1...m]) = LCS(x[1...n-1],Y[1....m-1])+1
如果Xn != Xm
LCS(X[1....n],Y[1...m]) = max{LCS(x[1...n],Y[1....m-1]), LCS(x[1...n-1],Y[1....m])}
以上边字串为例进行分析如图:
状态转移方程:
如果Xn == Ym
dp(X[1....n],Y[1...m]) = dp(x[1...n-1],Y[1....m-1])+1
如果Xn != Xm
dp(X[1....n],Y[1...m]) = max{dp(x[1...n],Y[1....m-1]), dp(x[1...n-1],Y[1....m])}
状态:给定的两个序列的LCS 的长度
dp[n][m]:n表示第一个串的长度,m表示第二个串的长度,n行m列元素的值,记录的就是这两个串的LCS长度
如上图,递归从后往前进行遍历,回溯时,从前往后,当X[n] == Y[m]的时候,给dp[n][m]+1。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int cnt = 0;//用于代码测试
string str1 = "helloworld";
string str2 = "hlweord";
int **dp = nullptr;
int **path = nullptr;
int LCS01(string X,int n,string Y,int m)
{
if (n < 0 || m < 0)
{
return 0;
}
if (dp[n][m] > 0) //已经被求解的子问题
{
return dp[n][m];
}
cnt++;
if (X[n] == Y[m])
{
dp[n][m] = LCS01(X, n - 1, Y, m - 1) + 1;
path[n][m] = 1;
return dp[n][m];
}
else
{
int len1 = LCS01(X, n, Y, m - 1);
int len2 = LCS01(X, n - 1, Y, m);
if (len1 >= len2)
{
dp[n][m] = len1;
path[n][m] = 2;//往左走
}
else
{
dp[n][m] = len2;
path[n][m] = 3;//往右走
}
return dp[n][m];
}
}
int main()
{
int n = str1.size();
int m = str2.size();
dp = new int *[n]; //n 行
for (int i = 0; i < n; ++i)
{
dp[i] = new int[m];//m列
for (int j = 0; j < m; ++j)
{
dp[i][j] = -1;
}
}
path = new int *[n]; //n行
for (int i = 0; i < n; ++i)
{
path[i] = new int[m]();//m列
}
int size = LCS01(str1,n-1,str2,m-1);
cout << "LCS length: " << size << endl;
cout << "cnt: " << cnt << endl;
system("pause");
return 0;
}
结果显示如下:
LCS最长公共子序列
这个思路和我们解决最长公共子序列的长度一样,也是递归回溯,递归从后往前进行遍历,如最长公共子序列的长度的图中线连接的数字就是递归遍历的路线,回溯时,从前往后,打印这条路线上1所对应的值。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int cnt = 0;//用于代码测试
string str1 = "helloworld";
string str2 = "hlweord";
int **dp = nullptr;
int **path = nullptr;
int LCS01(string X,int n,string Y,int m)
{
if (n < 0 || m < 0)
{
return 0;
}
if (dp[n][m] > 0) //已经被求解的子问题
{
return dp[n][m];
}
cnt++;
if (X[n] == Y[m])
{
dp[n][m] = LCS01(X, n - 1, Y, m - 1) + 1;
path[n][m] = 1;
return dp[n][m];
}
else
{
int len1 = LCS01(X, n, Y, m - 1);
int len2 = LCS01(X, n - 1, Y, m);
if (len1 >= len2)
{
dp[n][m] = len1;
path[n][m] = 2;//往左走
}
else
{
dp[n][m] = len2;
path[n][m] = 3;//往右走
}
return dp[n][m];
}
}
void backStrace(string str1,int n,int m)
{
if (n < 0 || m < 0)
{
return;
}
if (path[n][m] == 1) //走对角线 回溯之后打印这个值
{
backStrace(str1, n - 1, m - 1);
cout << str1[n];
}
else
{
if (path[n][m] == 2)//往左走 向左递归
{
backStrace(str1, n, m - 1);
}
else
{ //path[n][m] == 3 往上走 向上递归
backStrace(str1, n - 1, m);
}
}
}
int main()
{
int n = str1.size();
int m = str2.size();
dp = new int *[n]; //n 行
for (int i = 0; i < n; ++i)
{
dp[i] = new int[m];//m列
for (int j = 0; j < m; ++j)
{
dp[i][j] = -1;
}
}
path = new int *[n]; //n行
for (int i = 0; i < n; ++i)
{
path[i] = new int[m]();//m列
}
int size = LCS01(str1,n-1,str2,m-1);
backStrace(str1, n-1, m-1);
cout << endl;
system("pause");
return 0;
}
结果如下: