【简单dp】2080->最长公共子序列问题 动态规划

最长公共子序列问题

关于思路

因为比较菜所以只能写出dp的一些皮毛

我们用Ax表示序列A的连续前x项构成的子序列,即Ax= a1,a2,……ax, By= b1,b2,……by, 我们用LCS(x, y)表示它们的最长公共子序列长度,那原问题等价于求LCS(m,n)。为了方便我们用L(x, y)表示Ax和By的一个最长公共子序列。让我们来看看如何求LCS(x, y)。我们令x表示子序列考虑最后一项

  1. Ax = By
    那么它们L(Ax, By)的最后一项一定是这个元素!
    如果我们从序列Ax中删掉最后一项ax得到Ax-1,从序列By中也删掉最后一项by得到By-1,(多说一句角标为0时,认为子序列是空序列),则我们从L(x,y)也删掉最后一项t得到的序列是L(x – 1, y - 1)。因此L(x, y) = L(x - 1, y - 1) 最后接上元素t。可以得到LCS(Ax, By) = LCS(x - 1, y - 1) + 1
  2. Ax ≠ By
    仍然设t = L(Ax, By), 或者L(Ax, By)是空序列(这时t是未定义值不等于任何值)。则t ≠ Ax和t ≠ By至少有一个成立,因为t不能同时等于两个不同的值嘛!
    (2.1)如果t ≠ Ax,则有L(x, y)= L(x - 1, y),因为根本没Ax的事嘛。
    LCS(x,y) = LCS(x – 1, y)
    (2.2)如果t ≠ By,l类似L(x, y)= L(x , y - 1)
    LCS(x,y) = LCS(x, y – 1)

可是,我们事先并不知道t,由定义,我们取最大的一个,因此这种情况下,有LCS(x,y) = max(LCS(x – 1, y) , LCS(x, y – 1))。
看看目前我们已经得到了什么结论:
LCS(x,y) =
(1) LCS(x - 1,y - 1) + 1 如果Ax = By
(2) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By
这时一个显然的递推式,光有递推可不行,初值是什么呢?
显然,==一个空序列和任何序列的最长公共子序列都是空序列!==所以我们有:

在这里插入图片描述也可以用图示演示一波:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述综上所以我们可以得到最关键的最核心的伪代码:

for x = 0 to n do
    for y = 0 to m do
        if (x == 0 || y == 0) then 
            LCS(x, y) = 0
        else if (Ax == By) then
            LCS(x, y) =  LCS(x - 1,y - 1) + 1
        else 
            LCS(x, y) = ) max(LCS(x – 1, y) , LCS(x, y – 1))

注意: 我们这里使用了循环计算表格里的元素值,而不是递归,如果使用递归需要已经记录计算过的元素,防止子问题被重复计算。

关于代码

有了上面的分析就可以轻松解决最长公共子序列的问题了附上sdut oj上2080题最长公共子序列的代码:2080->最长公共子序列问题

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define max(a, b) (((a) > (b)) ? (a):(b))//max函数定义方法一
int dp[505][505];
/*int max(int a, int b)//max函数定义方法二
{
    if(a <= b)
        return b;
    else
        return a;
}*/
int main()
{
   
    int i, j, t, n, m;
    char a[505], s[505];
    while(~scanf("%s %s", a+1, s+1))//让字符串从1开始主要为了防止下标越界。
    {
   
        n = strlen(a+1);
        m = strlen(s+1);
        memset(dp, 0, sizeof(dp));
        for(i = 1; i <= n; i++)
        {
   
            for(j = 1; j <= m; j++)
            {
   
                if(a[i]==s[j])//如果字符对应相等直接让dp[i][j]前一个加一。
                    dp[i][j] = dp[i-1][j-1] + 1;
                else//如果不等就让它等于前一个里最大的那个。
                    dp[i]
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值