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
请按任意键继续. . .
原文地址:https://www.cnblogs.com/lovesaber/p/2034063.html