当前位置: 首页 > 编程日记 > 正文

触类旁通,经典面试题最长公共子序列应该这么答

640?wx_fmt=jpeg

作者 |  labuladong

来源 | labuladong(ID:labuladong)

【导读】最长公共子序列(Longest Common Subsequence,简称 LCS)是一道非常经典的面试题目,因为它的解法是典型的二维动态规划,大部分比较困难的字符串问题都和这个问题一个套路,比如说编辑距离。而且,这个算法稍加改造就可以用于解决其他问题,所以说 LCS 算法是值得掌握的。

题目就是让我们求两个字符串的 LCS 长度:

输入: str1 = "abcde", str2 = "ace" 	
输出: 3  	
解释: 最长公共子序列是 "ace",它的长度是 3

肯定有读者会问,为啥这个问题就是动态规划来解决呢?因为子序列类型的问题,穷举出所有可能的结果都不容易,而动态规划算法做的就是穷举 + 剪枝,它俩天生一对儿。所以可以说只要涉及子序列问题,十有八九都需要动态规划来解决,往这方面考虑就对了。

下面就来手把手分析一下,这道题目如何用动态规划技巧解决。

一、动态规划思路

第一步,一定要明确 dp 数组的含义。对于两个字符串的动态规划问题,套路是通用的。

比如说对于字符串 s1 和 s2,一般来说都要构造一个这样的 DP table:

640?wx_fmt=png

为了方便理解此表,我们暂时认为索引是从 1 开始的,待会的代码中只要稍作调整即可。其中,dp[i][j] 的含义是:对于 s1[1..i] 和 s2[1..j],它们的 LCS 长度是 dp[i][j]。

比如上图的例子,d[2][4] 的含义就是:对于 "ac" 和 "babc" ,它们的 LCS 长度是 2。我们最终想得到的答案应该是dp[3][6]。

第二步,定义 base case。

我们专门让索引为 0 的行和列表示空串,dp[0][..]和dp[..][0]都应该初始化为 0,这就是 base case。

比如说,按照刚才 dp 数组的定义,dp[0][3]=0的含义是:对于字符串""和"bab",其 LCS 的长度为 0。因为有一个字符串是空串,它们的最长公共子序列的长度显然应该是 0。

第三步,找状态转移方程。

这是动态规划最难的一步,不过好在这种字符串问题的套路都差不多,权且借这道题来聊聊处理这类问题的思路。

状态转移说简单些就是做选择,比如说这个问题,是求 s1 和 s2 的最长公共子序列,不妨称这个子序列为 lcs。那么对于 s1 和 s2 中的每个字符,有什么选择?很简单,两种选择,要么在 lcs 中,要么不在。

640?wx_fmt=png

这个「在」和「不在」就是选择,关键是,应该如何选择呢?这个需要动点脑筋:如果某个字符应该在lcs中,那么这个字符肯定同时存在于s1和s2中,因为lcs是最长公共子序列嘛。所以本题的思路是这样:

用两个指针 i 和 j 从后往前遍历 s1 和 s2 ,如果 s1[i]==s2[j],那么这个字符一定在 lcs 中;否则的话,s1[i] 和 s2[j] 这两个字符至少有一个不在 lcs 中,需要丢弃一个。先看一下递归解法,比较容易理解:

640?wx_fmt=png

对于第一种情况,找到一个 lcs 中的字符,同时将 i, j 向前移动一位,并给 lcs的长度加一;对于后者,则尝试两种情况,取更大的结果。

其实这段代码就是暴力解法,我们可以通过备忘录或者 DP table 来优化时间复杂度,比如通过前文描述的 DP table 来解决:

640?wx_fmt=png


二、疑难解答

对于 s1[i] 和 s2[j] 不相等的情况,至少有一个字符不在 lcs 中,会不会两个字符都不在呢?比如下面这种情况:

640?wx_fmt=png

所以代码是不是应该考虑这种情况,改成这样:

if str1[i - 1] == str2[j - 1]:	# ...	
else:	dp[i][j] = max(dp[i-1][j], 	dp[i][j-1],	dp[i-1][j-1])

我一开始也有这种怀疑,其实可以这样改,也能得到正确答案,但是多此一举,因为 dp[i-1][j-1] 永远是三者中最小的,max 根本不可能取到它。

原因在于我们对 dp 数组的定义:对于 s1[1..i]和s2[1..j],它们的 LCS 长度是dp[i][j]。

640?wx_fmt=png

这样一看,显然dp[i-1][j-1]对应的lcs长度不可能比前两种情况大,所以没有必要参与比较。

三、总结

对于两个字符串的动态规划问题,一般来说都是像本文一样定义 DP table,因为这样定义有一个好处,就是容易写出状态转移方程,dp[i][j] 的状态可以通过之前的状态推导出来:

640?wx_fmt=png

找状态转移方程的方法是,思考每个状态有哪些「选择」,只要我们能用正确的逻辑做出正确的选择,算法就能够正确运行。

原文链接:

https://mp.weixin.qq.com/s/myJbSMpOkh2zCPoY4q3duw

(*本文为 AI 科技大本营转载文章,转载请联系原作者)

福利时刻

入群参与每周抽奖~

扫码添加小助手,回复:大会,加入福利群,参与抽奖送礼!

640?wx_fmt=jpeg

AI ProCon 大会优惠票限时抢购中!识别海报二维码,即刻购票~

640?wx_fmt=png

推荐阅读

  • IBM重磅开源Power芯片指令集?国产芯迎来新机遇?

  • KDD 2019高维稀疏数据上的深度学习Workshop论文汇总

  • 说出来你可能不信,现在酒厂都在招算法工程师

  • 姚班三兄弟3万块创业八年,旷视终冲刺港股

  • 2019 AI ProCon日程出炉:Amazon首席科学家李沐亲授「深度学习」

  • AI Top 30+案例评选等你来秀!

  • 福利 | 马上为你安排和大咖面对面交流的机会,不可错过

  • 92年小哥绞尽脑汁骗得价值800万比特币, 破案后警方决定还给受害者

  • 他是叶问制片人也是红色通缉犯, 他让泰森卷入ICO, 却最终演变成了一场狗血的罗生门……

640?wx_fmt=png

你点的每个“在看”,我都认真当成了喜欢

相关文章:

两分公支的IPSec***流量走总部测试

一.概述:在论坛上看到一个朋友发帖希望两个分支的IPSEC ***流量经过总部,如是搭建拓扑测试了一下,因为跑两个VM版的ASA8.42机器性能不过,所以用PIX8.0来代替ASA,应该主要配置都跟ASA8.0差不多。二.基本思路:A.两个分支…

OpenCV代码提取:cvtColor函数的实现

OpenCV中的cvtColor函数包括了很多颜色格式之间的转换,用起来很方便,这里对cvtColor函数的code进行了提取,经测试,和OpenCV3.1结果完全一致。实现代码cvtColor.hpp:// fbc_cv is free software and uses the same licence as Open…

关于java.util.LinkedHashMap cannot be cast to ......的解决办法

今天在项目中遇到一个问题,接口接收到list在对list进行遍历的时候报出如下错误: 断点看一下这个list感觉没有任何的问题: 那为什么会报这个错误呢 这个接口是这样的,在想会不会是json在转list的时候把这个list给整坏了。 于是,我把这个list再…

三两下实现NLP训练和预测,这四个框架你要知道

作者 | 狄东林 刘元兴 朱庆福 胡景雯编辑 | 刘元兴,崔一鸣来源 | 哈工大SCIR(ID:HIT_SCIR)引言随着人工智能的发展,越来越多深度学习框架如雨后春笋般涌现,例如PyTorch、TensorFlow、Keras、MXNet、Theano 和 PaddlePaddle 等。这…

大学计算机基础实验

下载2013算法实验报告.rar转载于:https://www.cnblogs.com/shajianheng/p/3381968.html

java基础(十三)-----详解内部类——Java高级开发必须懂的

java基础(十三)-----详解内部类——Java高级开发必须懂的 目录 为什么要使用内部类内部类基础静态内部类 成员内部类 成员内部类的对象创建继承成员内部类局部内部类推荐博客匿名内部类正文 可以将一个类的定义放在另一个类的定义内部,这就是内部类。 回到顶部为什么…

C++中函数指针的使用

A function pointer is a variable that stores the address of a function that can later be called through that function pointer. This is useful because functions encapsulate behavior.函数指针是一个指向函数的指针,函数指针表示一个函数的入口地址。指针是变量&…

只做好CTR预估远不够,淘宝融合CTR、GMV、收入等多目标有绝招

作者 | 吴海波转载自知乎用户吴海波【导读】一直以来,电商场景就存在 ctr、cvr、gmv、成交 uv 等多个目标,都是核心指标。理想情况下,提升 ctr 就能提升 gmv,但本文作者认为,在一定程度上, ctr 和 gmv 并不…

Android监听HOME按键

2019独角兽企业重金招聘Python工程师标准>>> <!-- lang: java --> class HomeKeyEventBroadCastReceiver extends BroadcastReceiver {static final String SYSTEM_REASON "reason";static final String SYSTEM_HOME_KEY "homekey";// …

OpenCV代码提取:merge/split函数的实现

对OpenCV中的merge/split函数进行了实现&#xff0c;经测试&#xff0c;与OpenCV3.1结果完全一致。merge实现代码merge.hpp&#xff1a;// fbc_cv is free software and uses the same licence as OpenCV // Email: fengbingchun163.com#ifndef FBC_CV_MERGE_HPP_ #define FBC_…

DeepMind提图像生成的递归神经网络DRAW,158行Python代码复现

作者 | Samuel Noriega译者 | Freesia编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】最近&#xff0c;谷歌 DeepMInd 发表论文( DRAW: A Recurrent Neural Network For Image Generation&#xff09;&#xff0c;提出了一个用于图像生成的递归神…

其他进制的数字

JS中如果需要表示16进制的数字,则需要以0X开头 0X10 八进制数字以0开头 070 070有些浏览器会以8进制解析,但是有些则用10进制解析,10进制为70,8进制为56 所以parseint() 第二个参数可以设定进制,比如 parseint(“070”,10)代表以10进制解析070 2进制以0b开头,但是不是所有浏览…

java中的移位运算符

移位运算符是在数字的二进制形式上进行平移。主要有左移&#xff08;<<&#xff09;、带符号右移&#xff08;>>&#xff09;以及无符号右移&#xff08;>>>&#xff09;。左移运算符&#xff08;<<&#xff09;的运算规则为&#xff1a;按二进制形…

C++11中nullptr的使用

在C语言中&#xff0c;NULL实际上是一个void* 的指针&#xff0c;然后把void* 指针赋值给其它类型的指针的时候&#xff0c;会隐式转换成相应的类型。而如果用一个C编译器来编译的时候是要出错的&#xff0c;因为C是强类型的&#xff0c;void* 是不能隐式转换成其它指针类型的。…

埃森哲、亚马逊和万事达卡抱团推出的区块链项目有何神通?

据外媒报道&#xff0c;今日埃森哲宣布了一项新的区块链项目&#xff0c;该项目为基于区块链的循环供应链&#xff0c;将与万事达卡和亚马逊共同合作。据官方介绍&#xff0c;这个基于区块链的循环供应链能够让客户识别供应链上的小规模供应商和种植者&#xff0c;例如&#xf…

小团队如何玩转物联网开发?

近几年来&#xff0c;物联网发展迅速&#xff1a;据中商产业研究院《2016——2021年中国物联网产业市场研究报告》显示&#xff0c;预计到2020年&#xff0c;中国物联网的整体规模将达2.2万亿元&#xff0c;产业规模比互联网大30倍。与之相反的是&#xff0c;物联网开发者在开发…

Build Boost C++ libraries for x32/x64 VC++ compilers on Windows

2019独角兽企业重金招聘Python工程师标准>>> Boost is a set of libraries for the C programming language that provide support for tasks and structures such as linear algebra, pseudorandom number generation, multithreading, image processing, regular …

C++11中auto的使用

在C语言中&#xff0c;就有了auto关键字&#xff0c;它被当作是一个变量的存储类型修饰符&#xff0c;表示自动变量(局部变量)。它不能被单独使用&#xff0c;否则编译器会给出警告。在C11标准中&#xff0c;添加了新的类型推导特性。在C 11中&#xff0c;使用auto定义的变量不…

攻和防谁更厉害?AI技术在恶意软件检测中的应用和对抗

AI技术的发展为网络安全带来新机遇的同时&#xff0c;黑客也在逐渐利用AI漏洞建立对抗样本以躲避攻击&#xff0c;双方在各自领域的更多尝试也将是AI技术发展的一场新博弈。那么&#xff0c;在应用中&#xff0c;如何利用AI检测技术与恶意软件展开对抗&#xff1f; 腾讯安全技术…

一文看懂机器学习中的常用损失函数

作者丨stephenDC编辑丨zandy来源 | 大数据与人工智能&#xff08;ID: ai-big-data&#xff09;导语&#xff1a;损失函数虽然简单&#xff0c;却相当基础&#xff0c;可以看做是机器学习的一个组件。机器学习的其他组件&#xff0c;还包括激活函数、优化器、模型等。本文针对机…

Using Apache2 with JBoss AS7 on Ubuntu

大体思路同《Using Apache Web Server with Jboss AS 7》一致&#xff0c;但在Ubuntu上的操作与之前有些区别。 这里仍然演示mod_proxy的配置。 首先加载相应的模块。Ubuntu中加载模块和卸载模块均可以通过命令操作&#xff0c;与其对应的命令分别是a2enmod和a2dismod。 启用…

OpenCV代码提取:rotate函数的实现

OpenCV中并没有直接提供实现rotate的函数&#xff0c;这里通过getRotationMatrix2D和warpAffine函数实现rotate&#xff0c;并增加了一个crop参数&#xff0c;用来判断是否进行crop。目前支持uchar和float两种类型&#xff0c;经测试&#xff0c;与OpenCV3.1结果完全一致。公式…

在 Node.js 中用子进程操作标准输入/输出

翻译&#xff1a;疯狂的技术宅原文&#xff1a;http://2ality.com/2018/05/chi... 本文首发微信公众号&#xff1a;jingchengyideng欢迎关注&#xff0c;每天都给你推送新鲜的前端技术文章 在本中&#xff0c;我们在 Node.js 中把 shell 命令作为子进程运行。然后异步读取这些进…

再见,Python 2.x

整理 | 屠敏来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;在技术的长河中&#xff0c;软件、工具、系统等版本的迭代本是常事&#xff0c;但由于使用习惯、版本的兼容性、易用性等因素&#xff0c;很多用户及开发者在使用或做开发的过程中&#xff0c;并不愿意及…

Android UI系列-----CheckBox和RadioButton(1)

主要记录一下CheckBox多选框和RadioGroup、RadioButton单选框的设置以及注册监听器 1.CheckBox 布局文件&#xff1a; <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android…

C++中struct的使用

C语言继承了C语言的struct&#xff0c;并且加以扩充。在C语言中struct是只能定义数据成员&#xff0c;而不能定义成员函数的。而在C中&#xff0c;struct类似于class&#xff0c;在其中既可以定义数据成员&#xff0c;又可以定义成员函数。结构类型是用户定义的复合类型&#x…

填报表中也可以添加 html 事件

在实际的项目开发中&#xff0c;填报表的应用十分广泛。 多数情况下&#xff0c;填报表会作为整个项目的一部分配合需求灵活使用&#xff0c;但有时也会受大项目环境的影响&#xff0c;产生一些特别的要求。比如&#xff0c;通常报表单元格的数据类型大多是文本&#xff0c;有时…

60+业内技术专家,9大核心技术专题,AI ProCon倒计时一周!

2018 年&#xff0c;由 CSDN 举办的第一届 AI 开发者大会喊出“只讲技术&#xff0c;拒绝空谈”&#xff0c;两天会议时间&#xff0c;国内外几十家顶尖科技企业讲述了其主流技术及其应用案例&#xff0c;真正引领国内开发者紧跟技术浪潮。一年过去&#xff0c;在你还未有所觉察…

密码学研究-数字签名

引入&#xff1a;提到签名&#xff0c;大家都不陌生&#xff0c;大家知道&#xff0c;重大的文件一般都要领导签名&#xff0c;来确保这个文件的真实有效。而一些比较重要的合同&#xff0c;比如买房的购房合同&#xff0c;都要盖“骑缝章”&#xff0c;这个骑缝章&#xff0c;…

C++11中shared_ptr的使用

在C中&#xff0c;动态内存的管理是通过一对运算符来完成的&#xff1a;new&#xff0c;在动态内存中为对象分配空间并返回一个指向该对象的指针&#xff0c;可以选择对对象进行初始化&#xff1b;delete&#xff0c;接受一个动态对象的指针&#xff0c;销毁该对象&#xff0c;…