最长公共子序列问题

程序员代码面试指南:

        给定两个字符串str1和str2,返回最长公共子序列,存在多个时返回任意一个即可。

动态规划:

        str1长度设为M,str2长度设为N,构建dp[M][N]。d[i][j]表示str1[0:i]和str2[0:j]的最长公共子序列的长度。

        1.第一列,d[i][0]表示str1[0:i]与str2[0]的最长公共子序列长度,str2只取了一个字符,dp[i][0]最大值是1,如果str1[i]==str2[0],令d[i][0] = 1。一旦d[i][0]设为1,其后的d[i+1:M][0]都为1。

        2.第一行同理。

        3.其他位置,d[i][j]有三种情况:

            (1) d[i-1][j];

            (2) d[i][j-1];

            (3) 如果str1[i]==str2[j],d[i-1][j-1] + 1;

            三者选最大值。

得到dp后,根据dp获取最长公共子序列。

        1. 从dp右下角开始,有三种移动方式:向上、向左、向左上。

        2. 如果dp[i][j]大于dp[i-1][j]和dp[i][j-1],说明计算dp[i][j]时,选择了dp[i-1][j-1]+1,且str1[i]==str2[j],属于最长公共子序列,向左上方移动;

        3. 如果dp[i][j]等于dp[i-1][j]或dp[i][j-1],说明str1[i]不属于最长公共子序列,向左或向上移动。同时等于两者时,可任选其中一个方向。


C++实现如下:

#include <string>
using namespace std;

void getLCS(const string &str1, const string &str2, string &common)
{
    if (str1.empty() || str2.empty())
    {
        return;
    }
    
    int **dp = new int*[str1.length()];
    for (int i = 0; i < str1.length(); i++)
    {
        dp[i] = new int[str2.length()];
    }

    dp[0][0] = (str1.at(0) == str2.at(0) ? 1 : 0);
    for (int i = 1; i < str1.length(); i++)
    {
        if (dp[i - 1][0] == 1)
        {
            dp[i][0] = 1;
        }
        else
        {
            dp[i][0] = (str1.at(i) == str2.at(0) ? 1 : 0);
        }
    }
    for (int j = 1; j < str2.length(); j++)
    {
        if (dp[0][j - 1] == 1)
        {
            dp[0][j] = 1;
        }
        else
        {
            dp[0][j] = (str1.at(0) == str2.at(j) ? 1 : 0);
        }
    }
    for (int i = 1; i < str1.length(); i++)
    {
        for (int j = 1; j < str2.length(); j++)
        {
            dp[i][j] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1];
            if (str1.at(i) == str2.at(j))
            {
                dp[i][j] = dp[i - 1][j - 1] + 1 > dp[i][j] ? dp[i - 1][j - 1] + 1 : dp[i][j];
            }
        }
    }

    int m = str1.length() - 1;
    int n = str2.length() - 1;
    int length = dp[m][n];
    char *result = new char[length + 1];
    memset(result, 0, (length + 1) * sizeof(char));
    int index = length - 1;
    while (index >= 0)
    {
        if (n > 0 && dp[m][n] == dp[m][n - 1])
        {
            n--;
        }
        else if (m > 0 && dp[m][n] == dp[m - 1][n])
        {
            m--;
        }
        else
        {
            result[index--] = str1.at(m);
            m--;
            n--;
        }
    }

    common = result;

    delete[] result;
    result = NULL;
    for (int i = 0; i < str1.length(); i++)
    {
        delete[] dp[i];
        dp[i] = NULL;
    }    
    delete[] dp;
    dp = NULL;
}

int main(int argc, char* argv[])
{
    string str1 = "1A2C3D4B56";
    string str2 = "B1D23CA45B6A";
    string common;

    getLCS(str1,str2,common);
    printf("%s\n", common.c_str());
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值