问题分析:
求两个数组的公共子序列。
考虑最长公共子序列问题如何分解成子问题,设A=“a0,a1,…,am-1”,B=“b0,b1,…,bm-1”,并Z=“z0,z1,…,zk-1”为它们的最长公共子序列。不难证明有以下性质:
(1) 如果am-1=bn-1,则zk-1=am-1=bn-1,且“z0,z1,…,zk-2”是“a0,a1,…,am-2”和“b0,b1,…,bn-2”的一个最长公共子序列;
(2) 如果am-1!=bn-1,则若zk-1!=am-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列;
(3) 如果am-1!=bn-1,则若zk-1!=bn-1,蕴涵“z0,z1,…,zk-1”是“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列。
这样,在找A和B的公共子序列时,如有am-1=bn-1,则进一步解决一个子问题,找“a0,a1,…,am-2”和“b0,b1,…,bm-2”的一个最长公共子序列;如果am-1!=bn-1,则要解决两个子问题,找出“a0,a1,…,am-2”和“b0,b1,…,bn-1”的一个最长公共子序列和找出“a0,a1,…,am-1”和“b0,b1,…,bn-2”的一个最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
求解:
引进一个二维数组c[][],用c[i][j]记录X[i]与Y[j] 的LCS 的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
我们是自底向上进行递推计算,那么在计算c[i,j]之前,c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。此时我们根据X[i] = Y[j]还是X[i] != Y[j],就可以计算出c[i][j]。
问题的递归式写成:
回溯输出最长公共子序列过程:
共有三种回溯输出公共自序列的方法。
//动态规划题目
//最长公共子序列
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
void Fun(int *p, int *q, int **r, int lengthp, int lengthq);
int main()
{
int lengthp;
int lengthq;
int *p, *q;
int **r; //保存最长公共子序列的长度
int i;
printf("请输入两个序列的长度\n");
scanf("%d %d", &lengthp, &lengthq);
p = (int *)malloc(sizeof(int) * lengthp);
assert(p != NULL);
printf("输入序列p的值\n");
for(i = 0; i < lengthp; i++)
scanf("%d", p+i);
q = (int *)malloc(sizeof(int) * lengthq);
assert(q != NULL);
printf("输入序列q的值\n");
for(i = 0; i < lengthq; ++i)
scanf("%d", q+i);
r = (int **)malloc(sizeof(int *) * (lengthp+1));
assert(r != NULL);
for(i = 0; i < lengthp+1; ++i)
{
r[i] = (int *)malloc(sizeof(int) * (lengthq+1));
assert(r[i] != NULL);
}
Fun(p, q, r, lengthp, lengthq);
free(p);
free(q);
for(i = 0; i < lengthp; i++)
free(r[i]);
return 0;
}
void Fun(int *p, int *q, int **r, int lengthp, int lengthq)
{
void Print(int *p, int *q, int **Res, int lengthp, int lengthq);
void Print1(int *p, int *q, int **Res, int lengthp, int lengthq, int max);
void Print2(int *p, int *q, int **Res, int lengthp, int lengthq, int max);
int **Res; //保存子序列的输出方向
int i, j;
int max = 0; //最长公共子序列的长度
Res = (int **)malloc(sizeof(int *) * (lengthp+1));
assert(Res != NULL);
for(i = 0; i < lengthp+1; ++i)
{
Res[i] = (int *)malloc(sizeof(int) * (lengthq+1));
assert(Res[i] != NULL);
}
for(i = 0; i < lengthp+1; ++i)
for(j = 0; j < lengthq+1; ++j)
Res[i][j] = 0;
for(i = 0; i < lengthp+1; ++i)
r[i][0] = 0;
for(i = 0; i < lengthq+1; ++i)
r[0][i] = 0;
for(i = 1; i < lengthp+1; ++i)
{
for(j = 1; j < lengthq + 1; ++j)
{
if(p[i-1] == q[j-1])
{
r[i][j] = r[i-1][j-1] + 1;
Res[i][j] = 0; //搜索左上角
}
else if(r[i-1][j] > r[i][j-1])
{
r[i][j] = r[i-1][j];
Res[i][j] = -1;//上方
}
else
{
r[i][j] = r[i][j-1];
Res[i][j] = 1;//左面
}
}
}
for(i = 0; i < lengthp+1; ++i) //观察结果
{
for(j =0; j < lengthq+1; ++j)
printf("%d ", r[i][j]);
printf("\n");
}
printf("\n");
for(i = 0; i < lengthp+1; ++i)//观察结果
{
for(j =0; j < lengthq+1; ++j)
printf("%2d ", Res[i][j]);
printf("\n");
}
printf("\n");
for(i = 0; i < lengthp+1; ++i)
for(j = 0; j < lengthq+1; ++j)
if(max < r[i][j])
max = r[i][j];
printf("最长公共子序列的长度为%d ", max);
//Print(p, q, Res, lengthp, lengthq);
//Print1(p, q, r, lengthp, lengthq, max);
Print2(p, q, Res, lengthp, lengthq, max);
for(i =0; i < lengthp+1; ++i)
free(Res[i]);
}
void Print(int *p, int *q, int **Res, int lengthp, int lengthq)
{
if(lengthp < 1 || lengthq < 1)
return;
if(Res[lengthp][lengthq] == 0)
{
Print(p, q, Res, lengthp-1, lengthq-1);
assert(p[lengthp-1] == q[lengthq-1]);
printf("%d ", p[lengthp-1]);
}
else if(Res[lengthp][lengthq] == -1)
{
Print(p, q, Res, lengthp-1, lengthq);
}
else if(Res[lengthp][lengthq] == 1)
{
Print(p, q, Res, lengthp, lengthq-1);
}
}
void Print1(int *p, int *q, int **Res, int lengthp, int lengthq, int max)
{
int *temp;
int i, j;
int m = max;
temp = (int *)malloc(sizeof(int) * m);
assert(p != NULL);
i = lengthp;
j = lengthq;
while(i >0 && j > 0 && m >= 0)
{
if(p[i-1] == q[j-1])
{
temp[m-1] = p[i-1];
i--;
j--;
m--;
}
else if(Res[i-1][j] > Res[i][j-1])
{
i--;
}
else
{
j--;
}
}
for(i = 0; i < max; ++i)
printf("%d ", temp[i]);
free(temp);
}
void Print2(int *p, int *q, int **Res, int lengthp, int lengthq, int max)
{
int *temp;
int i, j;
int m = max;
temp = (int *)malloc(sizeof(int) * m);
assert(p != NULL);
i = lengthp;
j = lengthq;
while(i >0 && j > 0 && m >= 0)
{
if(Res[i][j] == 0 && p[i-1] == q[j-1])
{
temp[m-1] = p[i-1];
i--;
j--;
m--;
}
else if(Res[i][j] == -1)
{
i--;
}
else if(Res[i][j] == 1)
{
j--;
}
}
for(i = 0; i < max; ++i)
printf("%d ", temp[i]);
free(temp);
}