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 请按任意键继续. . .