动态规划 dp03 最长公共子串问题 c代码

题目:

若序列Z是序列X的子序列,又是Y的子序列,则称Z是序列X与Y的公共子序列。例如序列"bcba"是序列"abcbdab"与"bdcaba"的公共子序列。
例如,给出序列X "hahafdreghsbacdba"和序列"acdbegshbdrabsa",如何求取这两个序列的最长公共子序列?

这道题具有典型的最优子结构特性,即它的最优解一定包含子问题的最优解,这么看使用动态规划来解决是比较合理的。动态规划解题步骤通常由三个部分构成

1. 分阶段。

2. 状态迁移方程。

3. 寻找最优解。

对于这道题,字符数组a存储序列X,数组b存储序列Y,设i, j分别为数组a, b的指针。m[i][j]表示序列a[0] ~ a[i]与序列b[0]~b[j]的最长公共子序列,当a[i] == b[i]时,俩字符相等,最长公共子序列等于m[i-1][j-1] + 1; 如果字符不等,则

m[i][j] = MAX(m[i-1][j], m[i][j-1]);这个就是状态迁移方程。得到了状态迁移方程,在开始递推之前,需要知道边界情况,这里的边界情况就是当i等于0时,对于任意的0 <= j <= len(b), m[i][j] = 0; 同理,当j = 0时,任意的0 <= i <= len(a), m[i][j] = 0。对于字符串而言,数组下标为0是有字符的,所以说这么算会带来处理上的不方便,既然顺序不方便那就是用倒序好了,假设m[i][j]表示序列a[i] ~ a[last]与序列a[j][last]的最长公共子序列长度,当i = len(a)的时候,任意的j值,m[i][j] = 0,这个也很好理解,i等于n,说明a里面没有字符,这时候对于任意的j值,m[i][j] = 0,同理对于j = len(b),此时任意的i值,m[i][j] = 0。通常使用动态规划解决问题的时候,都存在两种递推方式,顺序递推和逆序递推,具体使用哪种根据具体问题来决定。

得到状态迁移方程,就可以知道最优解了,即m[0][0];接下来就是打印最优解序列了,这个可以根据状态迁移方程来判断哪个字符在最优解序列中。

下面是该问题的c代码实现:

//最长公共子串的动态规划解法

#include <stdio.h>
#include <string.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

void main()
{
	int i, j, k, n, m[30][30] = {0};
	char astr[30+1] = "hsbafdreghsbacdba", bstr[30+1] = "acdbegshbdrabsa";

	n = strlen(astr);
	k = strlen(bstr);

	printf("string A : %s\n", astr);
	printf("string B : %s\n", bstr);
	
	//边界初始化
	for (i = 0; i < n; i++)
		m[i][k] = 0;
	for (j = 0; j < k; j++)
		m[n][j] = 0;

	//状态递推
	for (i = n - 1; i >= 0; i--)
	{
		for (j = k - 1; j >= 0; j--)
		{
			if (astr[i] == bstr[j])
				m[i][j] = m[i+1][j+1] + 1;
			else
				m[i][j] = MAX (m[i+1][j], m[i][j+1]);
		}
	}

	//打印最优解
	printf("最长公共子串长度为:%d\n", m[0][0]); 
	for (i = 0; i < n;)
	{
		for (j = 0; j < k;)
		{
			if (astr[i] == bstr[j]){
				printf("%c", astr[i]);
				i++;
				j++;
			}
			else
			{
				if (m[i][j] == m[i+1][j])
					i++;
				else
					j++;
			}
		}
	}
	printf("\n");
	return;
}

 

参考资料:

1. 数据结构 : C语言版/ 严蔚敏,吴伟民编著

=============================================================================================

Linux应用程序、内核、驱动开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值