动态规划 : LCS(最长公共子序列)

问题描述

求两字符序列的最长公共字符子序列。

补充说明:

  • 不强制要求子串的字符连续出现在原始的2个字符序列中。
  • 测试字符序列,比如,
NameValue
第1个字符序列 S[x]{‘A’, ‘B’, ‘C’, ‘B’, ‘D’, ‘A’, ‘B’}
第2个字符序列 W[y]{‘B’, ‘D’, ‘C’, ‘A’, ‘B’, ‘A’}

解决思路

确立状态方程

参考图1.表格所示,记 LCS(x, y) 为表格中的元素,该元素表示截止到第1序列中的字符‘x’和第2序列中的字符‘y’时已有多少个相同的字符个数,即公共子序列的长度。

这里写图片描述
图1.

LCS(x,y)=LCS(x1,y1)+1,LCS(x,y)=max(LCS(x1,y),LCS(x,y1)),LCS(0,0)=0,if S(x) = W(y)if S(x) != W(y)if x = 0 or y = 0 

图1.中箭头的方向表示当前的“公共子序列长度”继承自哪个前一个位置,这里,如果有

[LCS(x1,y)=LCS(x,y1), S(x) != W(y)]
的情况,则如 图1.所示,当前LCS(x, y)元素值优先继承自纵轴上方的那个(箭头↑)。由此,亦可以看出,最长公共子序列并非唯一的。

代码
///////////////////////////////////////////////////////////////
//
// @2017-10-13 DP : LCS 最长公共子序列 
// 时间复杂度 O(n*m)
// 空间复杂度 O(n*m)
// 
///////////////////////////////////////////////////////////////
// 头文件
#include <iostream>
#include <vector>

using namespace std;

//@2017-10-13 TY 需考虑使用变长度数组 , 考虑使用 vector 实现变长二维数组 
#define ArraySize 16
int dp[ArraySize][ArraySize]={0};

///////////////////////////////////////////////////////////////
// DP LCS API
void DpLCS(vector<char>::iterator pVecX, vector<char>::iterator pVecY, int x, int y);

///////////////////////////////////////////////////////////////
// 
int main(void)
{
    char cBufX[] = {'A', 'B', 'C', 'B', 'D', 'A', 'B'};
    char cBufY[] = {'B', 'D', 'C', 'A', 'B', 'A'};

    vector<char> vecX, vecY;

    // 赋初值
    for (int i=0; i<(sizeof(cBufX)/sizeof(char)); i++)
    {
          vecX.push_back(cBufX[i]);
          //cout << vecX[i] << " ";
    }

    for (int i=0; i<(sizeof(cBufY)/sizeof(char)); i++)
    {
          vecY.push_back(cBufY[i]);
          //cout << vecY[i] << " ";
    } 

    // DP : LCS 
    // 二维数组存储的是 状态方程 的值 
    for (int i=0; i<vecX.size(); i++)
    {
        for (int j=0; j<vecY.size(); j++)
        {
            if (vecX.at(i) == vecY.at(j))
            {
                dp[i+1][j+1] = dp[i][j]+1;
            }
            else
            {
                dp[i+1][j+1] = std::max(dp[i][j+1], dp[i+1][j]);
            }
        }
    }
    /*
    // 打印 表格 content 
    for (int i=0; i<ArraySize; i++)
    {
        for (int j=0; j<ArraySize; j++)
        {
             cout << dp[i][j] << ' ';      
        }
        cout << endl;
    } 
    */   
    // Graph 
    DpLCS(vecX.begin(), vecY.begin(), vecX.size(), vecY.size());

    system("pause");
    return 0;
}

///////////////////////////////////////////////////////////////
// @2017-10-13 DP LCS
// DP : LCS
// (1) 从数列结尾向前递归. 
// (2) 结束的边界条件是  return ;
void DpLCS(vector<char>::iterator pVecX, vector<char>::iterator pVecY, int x, int y) 
{
    if (x == 0 || y == 0)
        return ;
    if (*(pVecX+x-1) == *(pVecY+y-1))
    {
        DpLCS(pVecX, pVecY, x-1, y-1);
        cout << *(pVecX+x-1) << ' ';
    }
    else if (dp[x-1][y] == dp[x][y])
    {
         DpLCS(pVecX, pVecY, x-1, y);
    }
    else
    {
        DpLCS(pVecX, pVecY, x, y-1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值