从最长公共子序列问题学动态规划(C++代码)

        本文旨在通过对最长公共子序列问题的解题思路,带领大家了解动态规划思想。

问题描述

        最长公共子序列(Longest Common Subsequence,LCS),即求两个序列最长的公共子序列(可以不连续)。

思路

        举例:

s1 = "delete", s2 = "leet"

        要求两个序列的公共子序列,如果我们将两个原序列分为前面(n-1)长的子序列和一个最后元素值。很容易想到,在两个序列最后一个元素值相等时,当前问题的解是两个子序列的LCS解+1;而当两个序列最后一个元素值不相等时,当前问题的解为max(s1和s2[:-1]的LCS解,s1[:-1]和s2的LCS解)。之后,处理边界值。当有任意一个序列长度为0时,则LCS=0。据此思想可以得到如下的递归代码:

int lcs(string s1,string s2)
{
    int n1=s1.size(),n2=s2.size();
    if(n1==0||n2==0)
        return 0;//border handle
    if(s1[n1-1]==s2[n2-1])
        return lcs(s1.substr(0,n1-1),s2.substr(0,n2-1))+1;
    else
        return max(lcs(s1,s2.substr(0,n2-1)),lcs(s1.substr(0,n1-1),s2));
}

附上测试代码:

#include 
#include 
#include 
using namespace std;

int lcs(string s1,string s2)
{
    int n1=s1.size(),n2=s2.size();
    if(n1==0||n2==0)
        return 0;//border handle
    if(s1[n1-1]==s2[n2-1])
        return lcs(s1.substr(0,n1-1),s2.substr(0,n2-1))+1;
    else
        return max(lcs(s1,s2.substr(0,n2-1)),lcs(s1.substr(0,n1-1),s2));
}

int main()
{
    string s1="delete",s2="leet";
    printf("%d",lcs(s1,s2));
    return 0;
}

        但是递归的代码时间、空间复杂度太高,所以我们想到对其进行优化,我们可以使用一个二维数组对各个长度子序列的LCS结果进行记录。设s1、s2的长度分别为n1、n2,则可以用dp[n1][n2]来做LCS结果记录。其中dp[i][j]表示s1[0:i]和s2[0:j]的LCS结果。在这种情况下,我们需要先确定短序列的LCS结果,而后才能推出长序列的LCS,所以矩阵应该从i、j小的位置开始填充。据此思路写出代码:

int lcs(string s1,string s2)
{
    int n1=s1.size(),n2=s2.size();
    int dp[n1+1][n2+1];//多出的一行一列用于记录其中有一个序列为0的情况
    memset(dp,0,sizeof(dp));//init

    for(int i=0;i<=n1;i++)
        dp[i][0]=0;
    for(int j=0;j<=n2;j++)
        dp[0][j]=0;//其中一个序列长度为0的情况

    for(int i=0;i

        之后写的非递归版的LCS代码即使用了动态规划思想,在这个题目中,使用了二维数组对已经计算的子问题答案进行了保存,每个dp[i][j]为一个状态的答案。

你可能感兴趣的:(动态规划,算法)