算法系列笔记6(动态规划—最长公共子序列/串lcs和最长递增子序列(LIS))

       子序列要求元素顺序一致就可以了,而字串必须是连续的。如ABCBDAB与BDCABA两个字符串,最长公共子序列有BCBA、BDAB和BCAB, 而最长公共字串只有AB和BD<连续>。当然这里的求解只求一个,但通常是这样直接说求最长公共子串,子序列,准确的应该是之一。

最长公共子序列

法一:穷举法

       检查字符串x所有字序列,共有2^m个,检查它是否在y字符串中出现,每个需要O(n),时间复杂度为指数级的。

法二:动态规划(DP)

       将两个字符串x[1…m]和y[1…n]放在x轴和y轴方向上便得到一个二维数组c[i,j]来记录x[1…i]和y[1…j]最长公共子序列个数。

x[i]==y[j]的时候 c[i,j] = c[i-1,j-1]+1;当不相等的时候,c[i,j] = max{c[i-1, j], c[i,j-1]}.

采用自底向上的思想,这样时间复杂度就等于lcs独立子问题的个数O(mn),不然需要重复计算子问题,时间复杂度仍然为指数级的。

代码如下:

 

// 最长公共子序列(不连续)   需要有个标记数组用于回溯
void lcs_sequences(const char* str1, const char* str2, int len1, int len2)
{
	int **c = new int*[len1+1];
	int **b = new int*[len1+1];
	int i, j;
	for(i = 0; i < len1+1; i++)
	{
		c[i] = new int[len2+1];
		b[i] = new int[len2+1];
	}
	for(i = 0; i <= len1; i ++)
		for(j = 0; j <= len2; j++)
			c[i][j] = 0;
	for(i = 1; i <= len1; i++)
	{
		for(j = 1; j <= len2; j++)
		{
			if(str1[i-1] == str2[j-1])
			{
				c[i][j] = c[i-1][j-1] +1;
				b[i][j] = 0;  // 来自左上
			}
			else{
				if(c[i-1][j] > c[i][j-1]){
					c[i][j] = c[i-1][j];
					b[i][j] = 1;    // 来自上方
				}
				else{
					c[i][j] = c[i][j-1];   // 来自左方
					b[i][j] = 2;    // 来自上方
				}
			}
		}
	}
	cout << "最长公共子序列长度: " << c[len1][len2] << endl;

	// 回溯求解路径
	i = len1;
	j = len2;
	char *x = new char[c[len1][len2]];
	int k = 0;
	/*while(i > 0 && j > 0){
		if(b[i][j] == 0)   // 来自左上
		{
			x[k++] = str1[i-1];
			//cout << str1[i-1];
			i--;
			j--;
		}
		else if(b[i][j] == 1) i--;
		else j--;
	}*/

	// 不使用标记数组进行回溯  直接使用str1和str2及c[i][j]得出结果
	while(i > 0 && j > 0)
	{
		if(str1[i-1] == str2[j-1])
		{
			x[k++] = str1[i-1];
			i--;
			j--;
		}
		else if(c[i][j] == c[i][j-1]) j--;
		else i--;
	}


	cout << "the lcs_opt is: " ;
	for(i = c[len1][len2]-1; i >= 0 ; i--)
	{
		cout << x[i];
	}
	cout << endl;
	for(i= 0; i < len1; i++)
		delete[] c[i];
	delete []c;
	delete []x;
}

上面注释的代码,我们用标记数组来跟踪来源,当然也可以不适用标记数字,直接使用c[i,j]str1<

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值