算法导论_15.4最长公共子序列

一、 问题

已知有字符串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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值