0
点赞
收藏
分享

微信扫一扫

DP——最长公共子序列LCS

五殳师兄 2022-04-04 阅读 56

1. 问题背景

在学习LCS之前,我们需要了解几个概念:
(1)子序列:设X=(x1, x2, … xm) 是一个序列, W = (w1, w2, … wm) 是 X 的一个子序列,如果存在1≤i1<i2<…<ik<m, 使xi j= wj 。 也就是说W = (w1, w2, … wm) 即是X=(x1, x2, … xm)中删除一些元素以后得到的序列。
在这里插入图片描述
(2)公共子序列:W是序列X与Y的公共子序列,如果W是X的子序列且W是Y的子序列。
在这里插入图片描述

2. 问题描述(LCS):

输入:X=(x1, x2, … xn),Y=(y1, y2, …ym)
输出:X与Y的最长公共子序列W

3. 题解:

我们先进行一下最长公共子序列优化解结构分析,如下图:
最长公共子序列优化解结构分析:
定理1(LCS的优化结构)设X=(x1 , ... xm), Y=(y1 , y2 , ... yn)是两个序列,W=(z1 , z2 , ... zk) 是X与Y的LCS。下列结论成立:
① 如xm= yn, 则wk=xm=yn, Wk-1是Xm-1和Yn-1的LCS,即,LCS(X,Y)=LCS(Xm-1,Yn-1)+xm
② 若xm  yn,且wkxm,则W是Xm-1和Y的LCS,即LCS(X,Y)=LCS(Xm-1
,Y)
③ 若xm  yn
, 且wk  yn
,则W是X与Yn-1的LCS,即
LCS(X,Y)=LCS(X,Yn-1
)
在这里插入图片描述
如果理解了上述定理,我们就可以很容易的写出规划方程:
在这里插入图片描述
我们可以把i=0或j=0的情况视为边界条件,即最开始序列中没有元素的时候LCS肯定为0。我们通过下面的例子来深入了解一下此题的dp思想:
在这里插入图片描述
图中的箭头是存储这个数值是从哪个子问题衍生出来的,假如我们不仅仅要求出LCS的长度,还要求出LCS中的元素,即最长公共子序列究竟是什么的话,就需要有一个备忘录,来记录表格中的数值是从哪个子问题衍生出来的,就可以得到LCS。当然,答案可能是不唯一的,比如最后一个(最右下角)元素4,根据我们的规划方程,B≠A,即C[7,6] = max(C[6,6], C[7,5]),而显然会发现这两个值都是4,也就是memo中记录👆或者👈都可以,这就会因程序员定义的优先级不同而使LCS序列不同。

4. 代码:

//
// Created by 23011 on 4/4/2022.
//

//最长公共子序列
#include<cstdio>
#include <iostream>
#include<string>
#include<cstring>
#define MAX 1001
using namespace std;
int dp[MAX][MAX];
int main()
{
    int n;
    scanf("%d", &n);
    while(n--)
    {
        string a,b;
        cin >> a >> b;
        memset(dp,0,sizeof(dp));
        int len_a = a.size(), len_b = b.size();
        for(int i = 0;i < len_a; i++)
        {
            for(int j = 0; j < len_b; j++)
            {
                if(a.at(i) == b.at(j))
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                else
                    dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]);
            }
        }
        printf("%d\n",dp[len_a][len_b]);
        a.clear();
        b.clear();
    }
    return 0;
}
举报

相关推荐

0 条评论