题目
采用动态规划算法求下列两个字符串的最长公共子序列:A="acgmood",B="agcmdoda"。
思路
最长公共子序列(Longest Common Subsequence,简称LCS)是指在两个序列中能够找到的最长的子序列,该子序列在原序列中的相对顺序保持不变,但不要求连续。换句话说,最长公共子序列是通过从两个序列中删除若干元素而形成的相同的元素序列,但是这些元素在原序列中的相对顺序保持不变。
求解最长公共子序列一般使用动态规划算法,具体步骤如下:
创建一个二维数组
dp
,其中dp[i][j]
表示第一个序列的前i
个元素和第二个序列的前j
个元素的最长公共子序列的长度。初始化边界条件:将第一行和第一列的所有元素设置为0,表示当一个序列的长度为0时,最长公共子序列的长度为0。
使用动态规划的思想进行状态转移:
如果第一个序列的第
i
个元素与第二个序列的第j
个元素相等(即A[i-1] == B[j-1]
),则dp[i][j] = dp[i-1][j-1] + 1
,表示当前元素可以作为最长公共子序列的一部分,长度加1。如果第一个序列的第
i
个元素与第二个序列的第j
个元素不相等(即A[i-1] != B[j-1]
),则dp[i][j]
的值取决于前一个状态,即dp[i][j] = max(dp[i-1][j], dp[i][j-1])
,表示当前元素不属于最长公共子序列,需要从前一个状态继承最长的长度。最终,
dp[m][n]
即为两个序列的最长公共子序列的长度,其中m
和n
分别为两个序列的长度。可以通过回溯的方式构造出最长公共子序列,从
dp[m][n]
开始,根据dp
数组的值和元素是否相等进行判断,逐步向左上方移动,将匹配的元素添加到最长公共子序列中。通过以上步骤,我们可以找到给定两个序列的最长公共子序列。
代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 100;
int dp[N][N]; //最长公共子序列的长度
string longestCommonSubsequence(string A, string B) {
int m = A.length();
int n = B.length();
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(A[i - 1] == B[j - 1]){
//因为A,B这两个字符串下标从0开始,dp数组下标从1开始,
//所以此处的判断条件是A[i - 1] == B[j - 1]
//也就是A字符串中第i个字符和B字符串中第j个字符相等
dp[i][j] = dp[i - 1][j - 1] + 1; //那么A中前i个字符和B中前j个字符的最长公共子序列
//就是A中前i-1个字符和B中前j-1个字符的最长公共子序列+1
}
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
int len = dp[m][n];
string lcs(len, ' ');
int i = m, j = n;
while(i > 0 && j > 0){
if(A[i - 1] == B[j - 1])
{
lcs[len - 1] = A[i - 1];
i--, j--, len--;
}
else if(dp[i - 1][j] > dp[i][j - 1])
i--;
else j--;
}
return lcs;
}
int main() {
string A = "acgmood";
string B = "agcmdoda";
string lcs = longestCommonSubsequence(A, B);
cout << "最长公共子序列: " << lcs << endl;
return 0;
}