一、 问题
已知有字符串str1 = “ABCBDAB”; 字符串str2 = “BDCABA”; 求字符串str1 和字符串str2的最长公共子序列。 上述两个字符串的最长公共子序列是BCBA;
将问题转化为一般问题:
字符串1:str[0], str[1], str[2], …, str[m-2], str[m-1];
字符串2:str[0], str[1], str[2], …, str[n-2], str[n-1];
从后面开始比较两个字符串:
1) 如果str[m-1] == str[n-1]; 则这个字符串必定是公共子串中的一个,记录下来之后再比较
字符串1:str[0], str[1], str[2], …,str[m-2] 和字符串2:str[0], str[1], str[2], …, str[n-2];
2) 如果str[m-1] != str[n-1]; 此时有两种情况
A) 比较:
字符串1:str[0], str[1], str[2], …,str[m-2], str[m-1];
字符串2:str[0], str[1], str[2], …, str[n-2];
B)比较
字符串1:str[0], str[1], str[2], …,str[m-2],
字符串2:str[0], str[1], str[2], …, str[n-2], str[n-1];
两种选择中的具有最长的公共子序列,是原问题的公共子序列
二、步骤
1) 步骤1:描述一个最长公共子序列
考虑使用穷举法解决问题, 从序列X中选出所有的可能子序列, 然后这个子序列与序列Y比较, 判断子序列是否是Y序列的一个子序列;这种方法时间复杂度是指数级别的;
LCS问题的最优子结构;
综上所述, 说明两个序列的一个LCS也包含了两个序列的前缀的一个LCS。 因此LCS问题具有最优子结构;
2) 步骤2:一个递归解
如果xm = yn, 需要在子问题空间中找到一个子问题, 即X(m-1), Y(n-1)的一个LCS最优解, 然后将xm = yn添加到这个子问题的最优解上面,得到原问题的最优解;
如果xm != yn, 需要在子问题空间中找到2个子问题, X(m-1), Yn的一个子问题 和 Xm, Y(n-1)的一个子问题。然后在这两个子问题中选择胶长的一个,再构造成原问题的子问题;
问题中的一个条件限制了我可能考虑的子问题。
当xi = yi的时候, 应当考虑X(i-1), Y(j-1)的子问题;
当xi != yj的时候, 应当考虑两个子问题:Xi , Y(j-1)和X(i-1), Yj;
3) 步骤3:
采用自顶向下的递归算法, 可以得到两个序列最长LCS的长度;
但是递归算法重复计算子问题,而实际上子问题空间中总共有m×n个子问题;
使用动态规划算法, 可以避免重复子问题;
动态规划是从自底向上的,因此需要初始值,边界条件就是动态规划的初始值;
动态规划求解代码如下:
void lcs_len(char* str1, char* str2) {
//考虑到X序列, Y序列的第一个字符, 如果相等,则nums[1, 1] = 1,
//则将边界条件定位i或是j等于0的时候, 其值为0
for (int i = 0; i <= m; i++)
nums[i][0] = 0;
for (int j = 0; j <= n; j++)
nums[0][j] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (str1[i - 1] == str2[j - 1]) {
nums[i][j] = nums[i - 1][j - 1] + 1;
path[i][j] = EQUAL;
}
else if(nums[i-1][j] > nums[i][j-1]){
nums[i][j] = nums[i - 1][j];
path[i][j] = UP;
}
else {
nums[i][j] = nums[i][j - 1];
path[i][j] = LEFT;
}
}
}
}
4) 步骤4:构造一个LCS
从path[m][n] 开始, 每当遇到与各EQUAL的时候,说明str1[i-1] == str2[j-1], 遇到UP或是LEFT的时候,需要再次递归调用;
int print_lcs(char* str, int i, int j) {
if (i == 0 || j == 0)
return 0;
if (path[i][j] == EQUAL) {
print_lcs(str, i - 1, j - 1);
printf("%c ", str[i - 1]);
}
else if (path[i][j] == UP)
print_lcs(str, i - 1, j);
else
print_lcs(str, i, j - 1);
}
三、主函数代码
int main()
{
char str1[] = {
'A','B','C','B','D','A','B' };
char str2[] = {
'B','D','C','A','B','A' };
lcs_len(str1, str2);
print_lcs(str1, 7, 6)