题目:
若序列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),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。