DP问题之最长公共子序列
问题描述:给定两个序列X=<x1, x2,x3,…,xm> 和 Y=<y1, y2, y3,…, yn>, 求X与Y的一个最长公共子序列
问题分析:这个题目的阶段不是明显,没有很明显的上一步、上一层之类的。
既然涉及到公共子序列,也就是有X的第 i 个字符和Y的第 j 个字符相等的情况。
显然如果X[i] = Y[j] 那么长度分别为 i 和 j 的最长公共子序列就是长度分别为 i-1 和 j-1的最长公共子序列 加上 X[i] 或 Y[j]。
如果X[i] != Y[j] 呢?
如果不相等,那么长度为 i 和长度为 j 的序列的最长公共子序列就是“长度为i-1 和 j ” 和“长度为 i 和 j-1 ”中最长公共子序列中较长的一个。
因此可以设计以一个状态opt[i, j] 表示起点为 1 ,长度分别为 i 和 j 的最长公共子序列,状态方程可以写为:
opt[i-1, j-1] + x[i] x[i] == y[j]
opt[i, j] = opt[i-1, j] x[i] != y[j] && Len(opt[i-1, j] ) >= Len(opt[i, j-1])
opt[i, j-1] x[i] != y[j] && Len(opt[i-1, j]) < Len(opt[i, j-1])
(0 <= i <= Len(X) && 0 <= j <= Len(Y))
测试代码:
#include <stdio.h>
#include <stdlib.h>
static const int m = 8;
static const int n = 6;
char X[8] = {' ','A','B','C','B','D','A','B'}; //X[0] 为了后面写代码方便
char Y[6] = {' ','B','D','C','B','A'};
int len[8][6] = {0}; //记录序列1的 i 和序列2的 j 的最长公共子序列的长度
int flag[8][6] = {0}; //记录在i和j处的选择,为后面输出最长子序列
int LCS()
{
int i, j;
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
if(X[i] == Y[j])
{
len[i][j] = len[i-1][j-1] + 1;
flag[i][j] = 0;
}
else if(len[i-1][j] >= len[i][j-1])
{
len[i][j] = len[i-1][j];
flag[i][j] = 1;
}
else
{
len[i][j] = len[i][j-1];
flag[i][j] = 2;
}
}
}
return len[m-1][n-1];
}
void printLCS(int i, int j)
{
if(i==0 || j==0)
return;
if(flag[i][j] == 0)
{
printLCS(i-1, j-1);
printf("%c ", X[i]);
}
else
{
if(flag[i][j] == 1)
printLCS(i-1, j);
else
printLCS(i, j-1);
}
}
int main()
{
printf("Longest Common Sequence Length: %d\n", LCS());
printLCS(m-1, n-1);
printf("\n");
return 0;
}
结果:
Longest Common Sequence Length: 4 B C B A 请按任意键继续. . .

浙公网安备 33010602011771号