[算法笔记]最长公共子序列

14 篇文章 3 订阅
订阅专栏

问题介绍

在这里插入图片描述

最长公共子序列(Longest Common Subsequence,LCS)的解法诸多,包括但不限于蛮力法和动态规划。但是由于诸多原因,它被算作是动态规划领域的经典问题

值得注意的是,子序列≠子串

子序列未必连续,子串必然连续。

例如abcbdadbcbd两个串的LCS是bcbd

  1. LCS可能等于某个串整体
  2. LCS可能有多个(不一定是唯一,但长度相同)

解析

动态规划的核心是状态转移方程,因此先给出状态转移方程:

dp[i][j]=0								// 边界条件:i=0或j=0
dp[i][j]=dp[i-1][j-1]+1					// a[i-1]=b[j-1]
dp[i][j]=MAX(dp[i][j-1],dp[i-1][j])		// a[i-1]≠b[j-1]

大致策略

网上的推导很多,篇幅都不短,其实就是在说一个问题,对于串A和串B和它们的最长公共子序列Z,看它们的结尾

//结尾相同且为Z的最后一个字符
abccd
afccd
//结尾不同
abbcd
abbc

既然是动态规划,目的就是找到子问题,逐步缩减规模。这里缩减的就是串A和串B的最长公共子序列Z的长度。

显然,对于结尾有两种可能性:

  1. 结尾相同且为Z的最后一个字符
abccd
afccd
// 子问题是:
abcc
afcc

由上,可以去掉最后一个字符得到子问题

  1. 结尾不同
abbcd
abbc
// 子问题是
abbc
abbc

对于

abbca
abbcd

这种序列,可以先对上方串基于上述策略去除结尾,再对下方串运用相同策略去除结尾,得到:

abbc
abbc

状态转移方程解析

首先整个dp[][]啥意思。以串acbbabdbbabcbdb为例:

在这里插入图片描述

整个边界给0,是方便后续的计算。

d[i][j]的意思就是串A从1到j个元素组成的串和串B从1到i个元素组成的串的LCS数值。

显然对于dp[1][j],也就是串B的第一个字符a,无论如何和是与a还是ac还是acb…一直到acbbabdbb,其LCS都只有一个,因此不难发现第一行全为1。

然后问题扩大(其实是划分子问题的逆向过程),在B串截取出ab,该串和a或是ac或是acb…一直到acbbabdbb求取LCS

显然在这个时候多了一个b,但这时不影响,我们可以根据加上这个b之前的信息,判断现在的情况。

也就是加上这个b之前,最长子序列的情况,加上b与后面的匹配情况。

比如说在这里B串的a和A串的第一个a匹配了,这时LCS等于1,然后在它之后,b若能和A已匹配序列之后的剩余序列中的某个匹配上,就给LCS加一

显然,这时b可以和acb的最后一个字符b匹配上。所以dp[2][3]变为了2


再回过头来看状态转移方程:

dp[i][j]=0								// 边界条件:i=0或j=0
dp[i][j]=dp[i-1][j-1]+1					// a[i-1]=b[j-1]
dp[i][j]=MAX(dp[i][j-1],dp[i-1][j])		// a[i-1]≠b[j-1]

发现第三条还没讲,那就看图的dp[5][3],即下图中标红处:

在这里插入图片描述

这时是B串拿abcbd和A串比较,在比到A串的第三个字符时(也就是acb),我们至少知道,B串的子串abcb和它的LCS为3。因此哪怕再给B的子串加一个,其LCS也至少为3。

// dp[5][3]表示的LCS对应的情况
acb
abcbd
// 退化为已知情况dp[4][3]
acb
abcb

如何根据dp表找序列

逆序求出,从右下角开始

在这里插入图片描述

int i = s2.length();
int j = s1.length();
while(i>=0 && j >= 0){
	if(dp[i][j]==dp[i-1][j]){
		i--;
	}
	else if(dp[i][j] == dp[i][j-1]){
		j--;
	}
	else{
		ret += s1[j-1];
		i--;j--;
	}
}

复杂度

时间复杂度 O ( m n ) O(mn) O(mn)

空间复杂度 O ( m n ) O(mn) O(mn)

其中 m m m n n n分别是两个串的长度。

类似题目

[牛客]BM65 最长公共子序列(二)


class Solution {
  public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    int dp[2001][2001];
    string LCS(string s1, string s2) {
        string ret = "";
        int max = 0;
        // 边界条件置零
        for (int i = 0; i < 2001; i++) {
            dp[i][0] = 0;
        }
        for (int i = 0; i < 2001; i++) {
            dp[0][i] = 0;
        }
        for (int i = 1; i <= s2.length(); ++i) {
            for (int j = 1; j <= s1.length(); ++j) {
                if (s1[j - 1] == s2[i - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = dp[i][j - 1] > dp[i - 1][j] ? dp[i][j - 1] : dp[i - 1][j];
                }
            }
        }
        int i = s2.length();
        int j = s1.length();
        while (i >= 0 && j >= 0) {
            if (dp[i][j] == dp[i - 1][j]) {
                i--;
            } else if (dp[i][j] == dp[i][j - 1]) {
                j--;
            } else {
                ret += s1[j - 1];
                i--;
                j--;
            }
        }
        int n = ret.length();
        if(n == 0){
            return "-1";
        }
        for (int i = 0; i < n / 2; i++)
            swap(ret[i], ret[n - i - 1]);
        return ret;
    }
};

然后,例行自我剖析,跟大佬代码比较一下(我是说常规思路)

class Solution {
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        int len1 = s1.length() + 1;
        int len2 = s2.length() + 1;
        string res = "";
        vector<vector<int> > dp(len1, vector<int>(len2, 0));
        for (int i = 1; i < len1; ++i)
            for (int j = 1; j < len2; ++j)
                if (s1[i - 1] == s2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);

        int i = len1 - 1, j = len2 - 1;
        while (dp[i][j]) {
            if (dp[i-1][j] == dp[i][j-1] && dp[i][j] > dp[i-1][j-1]) {
                res += s1[i - 1];
                --i;
                --j;
            }
            else if (dp[i - 1][j] > dp[i][j - 1])  --i;
            else --j;
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

它用的是vector,顺手说一下

vector<vector<int> > dp(len1, vector<int>(len2, 0));

使用的构造函数是

//定义具有5个整型元素的vector,且每个元素初值为2
vector<int>a(5,2);

其实就是一个置零的操作。


然后是另一个大佬的操作,这个效率是真的高:

static const auto io_sync_off = []()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return nullptr;
}();


class Solution {
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        if(s1.empty()||s2.empty()) return "-1";
        vector<vector<int> > hashTable(128,vector<int>());
        vector<int> A;
        for(int i=0;i<s1.size();i++)
            hashTable[s1[i]].push_back(i);
        for(int i=0;i<s2.size();i++)
            for(int j=hashTable[s2[i]].size()-1;j>=0;j--)
                A.push_back(hashTable[s2[i]][j]);
         
        int N = A.size(), topSize=1;
        if(!N) return "-1";
         
        vector<int> top(N,0), topIndexs(N,0), pre(N,0);
        top[0]=A[0];
        for(int i=0;i<N;i++)
        {
            if(A[i]>top[topSize-1])
            {
                pre[i] = topIndexs[topSize-1];
                top[topSize] = A[i];
                topIndexs[topSize++] = i;
            }
            else
            {
                int pos = lower_bound(top.begin(),top.begin()+topSize,A[i])-top.begin();
                if(pos) pre[i] = topIndexs[pos-1];
                top[pos]=A[i];
                topIndexs[pos]=i;
            }
        }
         
        int endIndex = topIndexs[topSize-1];
        string seq(topSize,0);
        for(int i = topSize-1,s=endIndex;i>=0;i--,s=pre[s])
            seq[i]=s1[A[s]];
         
        return seq;
    }
};


c语言最长公共子序列,算法设计与分析/动态规划——最长公共子序列LCS及模板...
weixin_30824391的博客
05-19 1262
这位大佬写的对理解DP也很有帮助,我就直接摘抄过来了,代码部分来自我做过的题一,问题描述给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA二,算法求解这是一个动态规划的题目。对于可用动态规划求解的问题,一般有两个特征:①最优子结构;...
最长公共子序列动态规划c语言,动态规划----最长公共子序列(C++实现)
weixin_39912566的博客
05-22 2317
最长公共子序列题目描述:给定两个字符串s1 s2 … sn和t1 t2 … tm 。求出这两个字符串的最长公共子序列的长度。字符串s1 s2 … sn的子序列指可以表示为 … { i1 < i2 < … < ik }的序列。输入样例2asdfadfsd123abcabc123abc输出样例36解题思路:这道题是被称为最长公共子序列的问题(LCS,Longest Common S...
动态规划 - 最长公共子序列 - 最长公共子串 - 最长不重复子串 - 最长递增子序列 - 最长回文子串
星星之火
06-05 1982
源自:http://blog.csdn.net/chuan6099/article/details/8952978 动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有
最长公共子序列(LCS)
最新发布
a09learnc的博客
09-19 219
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0。输入为两行每行一个字符串。输出最长公共子序列的长度。
LCS(最长公共子序列
dianlu7964的博客
09-05 140
继续动态规划O(∩_∩)O~暑假之前就写了的,只是去实习了,没来及贴上来。关于动态规划的基础知识,参见算法与数据结构之前的文章,(其实我也要再看看,当时理解的自认为还比较深刻,现在又忘得差不多了)LCS问题描述,就不说了,网上一大堆。如果看了我前面的文章,怎样来用动态规划解决,也应该很简单了。不废话,动态规划的难点在于问题刻画和发现最优子结构,以及怎样逆向思维(居然还记得这么多,,,):...
牛客网:最长公共子序列
张宜强的博客
09-21 657
https://www.nowcoder.com/practice/9ae56e5bdf4f480387df781671db5172?tpId=3&amp;&amp;tqId=10892&amp;rp=1&amp;ru=/activity/oj&amp;qru=/ta/hackathon/question-ranking 题目描述 我们有两个字符串m和n,如果它们的子串a和b内容相同,则称a和...
LCS算法
热门推荐
leohxj的专栏
11-11 2万+
<br /><br />首先将要看到如何运用动态编程查找两个 DNA 序列的最长公共子序列(longest common subsequence,LCS)。发现了新的基因序列的生物学家通常想知道该基因序列与其他哪个序列最相似。查找 LCS 是计算两个序列相似程度的一种方法:LCS 越长,两个序列越相似。<br />子序列中的字符与子字符串中的字符不同,它们不需要是连续的。例如,ACE 是 ABCDE 的子序列,但不是它的子字符串。请看下面两个 DNA 序列:S1 = DE>GCCCTAGCGDE>S2 = 
算法最长公共子序列(做法小计)
m0_73755253的博客
06-27 661
给出长度为n的数组a,长度为m的数组b,求解其最长公共子序列
算法最长公共子序列
gjnm820的博客
05-21 2601
好久没做算法题了,现在发现自己的算法能力非常薄弱,所以特意练练,顺便做个笔记方便以后查看。 今天整理一下最长公共子序列最长公共子序列的问题常用于解决字符串的相似度,是一个非常实用的算法,作为码农,此算法是我们的必备基本功。 一、最长公共子序列概念: 最长公共子序列,是指两个字符串可具有的长度最大的公共的子序列。听着好像有点绕口,举个例子吧: 上面标红的就是最长公共子序列,是不
算法笔记——求最长公共子序列(动态规划法)
paidaxinga的博客
10-24 385
最长公共子序列 #include<stdio.h> #define Max 50 int c[Max][Max]; int CommonOrder(int m, int n, char x[],char y[],char z[]) { for (int i = 0; i <= m; i++) { for (int j = 0; j <= n; j++) { if (i == 0 || j == 0) { c[i][j] = 0; } else
MIT算法导论公开课之课程笔记 15.动态规划、最长公共子序列.rar
07-06
通过阅读这份笔记,学习者可以理解动态规划的基本概念,掌握如何使用动态规划解决最长公共子序列问题,并能应用到实际编程任务中。同时,对于那些对算法分析有兴趣的开发者,这将是一份宝贵的参考资料,有助于提升...
最长公共子序列算法总结
09-30
最长公共子序列的两种算法简介,一种是平时最常见的算法,还有一种用滚动数组来做的。
【完虐算法】「字符串-最长公共子序列」全面总结
2401_83916283的博客
04-18 342
上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。很多人担心学了容易忘,这里教你一个方法,那就是重复学习。打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。
程序员的算法课(6)-最长公共子序列(LCS)
十步杀一人-千里不留行
08-21 5887
上一节我们讲了动态规划,我们也知道,动态规划对于子问题重叠的情况特别有效,因为它将子问题的解保存在存储空间中,当需要某个子问题的解时,直接取值即可,从而避免重复计算! 这一节我们来解决一个问题,就是最长公共子序列。 一、啥叫最长公共子序列? 【百度百科】LCS是Longest Common Subsequence的缩写,即最长公共子序列。一个序列,如果是两个或多个已知序列的子序列,且是所有子...
各类算法技巧核心代码,知识点归纳总结之最长公共子序列
weixin_30722589的博客
03-08 84
最长公共子序列核心 for(i=1;i<=len1;i++) for(j=1;j<=len2;j++) c[i][j]=a[i-1]==b[j-1]?c[i-1][j-1]+1:max(c[i-1][j],c[i][j-1]);//“=”的运算等级要高于“==”; 压缩后核心代码只有三行,天啊,多么优美...
最长公共子序列
云卷云舒
05-20 419
在两个字符串中,有些字符会一样,可以形成的子序列也有可能相等,因此,长度最长的相等子序列便是两者间的最长公共字序列。例如序列X=''ABCBDAB''与 序列Y =''BDCABA'',最长公共子序列为“BCDA”. 基本概念: 子序列(subsequence): 一个特定序列的子序列就是将给定序列中零个或多个元素去掉后得到的结果(不改变元素间相对次序)。例如序...
最长公共子序列
10-30 243
辣鸡csdn换了些正常的广告很欣慰。QwQ lcs问题很经典 在两个序列中寻找最长的不需要连续的相同的序列 如果对序列没有任何要求的话大多数人都用网上流传已久的NM做法 dp[i][j]表示第一个序列到第i位,第二个序列到第j位的最长的lcs if(s[i]==t[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); else dp[i][j]=max(dp
最长公共子序列(LCS)
moving
04-14 1万+
1、LCS基本概念          子序列:一个序列X任意删除若干个字符得到新序列Z,则Z叫做X的子序列。例如Z=是X=B,C,B,D,A,B>的子序列,相当于删除A、B、A。          公共子序列:给定两个序列X和Y,如果Z既是X的子序列,也是Y的子序列,我们称它为X和Y的公共子序列。公共子序列可能不唯一。例如是X=B,C,B,D,A,B>和Y=B,D,C,A,B,A>的一个公共子
力扣每日一题:1143. 最长公共子序列
aruewds的博客
04-03 270
目录题目:1143. 最长公共子序列示例1示例2示例3解题思路解题代码解题感悟 题目:1143. 最长公共子序列 难度: 中等 题目: 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。 两
算法设计与分析详尽笔记:递归、分治与动态规划详解
举例涉及矩阵连乘问题、最长公共子序列、凸多边形最优三角剖分、电路布线和流水平行作业调度等,这些都是动态规划的经典案例。在解决实际问题时,如0-1背包问题和最优二叉搜索树,学生需要理解并掌握最优子结构这一...
写文章

热门文章

  • [微积分笔记]第二类曲线/面积分总结 12985
  • [曲线积分笔记]第一类曲线积分 12608
  • 离散数学常见知识点 11360
  • 球面坐标系下的三重积分 10339
  • 术数基础背诵口诀整理 10256

分类专栏

  • Unity 9篇
  • 2篇
  • UE 8篇
  • Lisp语系 1篇
  • 数学 20篇
  • c/c++/c# 6篇
  • TA
  • Shader 3篇
  • 图形学 4篇
  • 数字图像处理 2篇
  • 编译原理 1篇
  • Lua 1篇
  • python 3篇
  • 术数 1篇
  • 数据结构与算法 14篇
  • 操作系统 13篇
  • 计算机组成原理 5篇
  • 计算机网络 3篇
  • 数据库 3篇

最新评论

  • [曲线积分笔记]第一类曲线积分

    不如行乐: 为什么当曲线关于x轴对称时看的是被积函数关于y的奇偶性而不是关于x的

  • 二重积分的解题技巧

    qq_52324008: 奇偶性下的例题12.4算错了,答案是“-π”才对

  • 球面坐标系下的三重积分

    微笑暖晴天: 高等数学里的θ φ定义与工程上的定义是相反的,这点得注意

  • [操作系统笔记]虚拟内存

    c_l1030: https://blog.csdn.net/qq_39377889/article/details/128302612

  • 一阶线性微分方程计算公式推导

    月影_弦奏: 妙极了

大家在看

  • 论文 Python 实现WOA-CNN-BiGRU-Attention数据分类预测 64
  • 手环押金原路退回系统——足浴店——东方仙盟

最新文章

  • UGUI动态元素大小的滑动无限列表
  • Unity笔记:ScrollRect代码阅读
  • 代码整洁之道读书笔记
2024年18篇
2023年24篇
2022年52篇
2021年3篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家江苏景区玻璃钢雕塑市场商场新年美陈拍摄商场马桶美陈创意商场前广场的美陈吸引商场美陈电源怎么解决永春玻璃钢花盆花器清远玻璃钢楼盘景观雕塑生产商场美陈效果图 网站鹿邑制作玻璃钢雕塑厂选哪家洛阳玻璃钢卡通雕塑设计卡通人物玻璃钢雕塑多少钱深圳金壁虎玻璃钢雕塑文山玻璃钢雕塑价格汕头玻璃钢雕塑座椅南通小提琴玻璃钢花盆玻璃钢马兰花花朵雕塑三门峡商场美陈造景江西多彩玻璃钢雕塑图片甘孜玻璃钢雕塑浦口美陈商场玻璃钢卡通雕塑阿狸订做南宁人物玻璃钢雕塑多少钱泡沫翻模玻璃钢雕塑教程菏泽商场美陈雕塑厂家浙江创意玻璃钢雕塑制作户外游乐场玻璃钢雕塑价格玻璃钢雕塑玻璃熊广州玻璃钢雕塑构件郑州不锈钢水景玻璃钢景观雕塑北关玻璃钢花盆花器香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化