一:作用

最长公共子序列的问题常用于解决字符串的相似度,是一个非常实用的算法,作为码农,此算法是我们的必备基本功。

二:概念

举个例子,cnblogs这个字符串中子序列有多少个呢?很显然有27个,比如其中的cb,cgs等等都是其子序列,我们可以看出子序列不见得一定是连续的,连续的那是子串。我想大家已经了解了子序列的概念,那现在可以延伸到两个字符串了,你可以看出 cnblogs 和 belong 的公共子序列吗?在你找出的公共子序列中,你能找出最长的公共子序列吗?

从图中可以看到最长公共子序列为blog,仔细想想我们可以发现其实最长公共子序列的个数不是唯一的,可能会有两个以上,但是长度一定是唯一的,比如这里的最长公共子序列的长度为4。

三:解决方案

1. 枚举法

这种方法是最简单,也是最容易想到的,当然时间复杂度也是龟速的,可以分析一下,刚才也说过了cnblogs的子序列个数有27个 ,延伸一下:一个长度为N的字符串,其子序列有2N个,每个子序列要在第二个长度为N的字符串中去匹配,匹配一次需要O(N)的时间,总共也就是O(N*2N),可以看出,时间复杂度为指数级,恐怖的令人窒息。

2. 动态规划

既然是经典的题目肯定是有优化空间的,并且解题方式是有固定流程的,这里我们采用的是矩阵实现,也就是二维数组。

  • 第一步:先计算最长公共子序列的长度。

  • 第二步:根据长度,然后通过回溯求出最长公共子序列。

现有两个序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},设一个C[i,j]: 保存Xi与Yj的LCS的长度。

递推方程为:

不知道大家看懂了没?动态规划的一个重要性质特点就是解决“子问题重叠”的场景,可以有效的避免重复计算,根据上面的公式其实可以发现C[i,j]一直保存着当前(Xi,Yi)的最大子序列长度,代码如下:

public class Program{static int[,] martix;static string str1 = "cnblogs";static string str2 = "belong";static void Main(string[] args){martix = new int[str1.Length + 1, str2.Length + 1];LCS(str1, str2);//只要拿出矩阵最后一个位置的数字即可Console.WriteLine("当前最大公共子序列的长度为:{0}", martix[str1.Length, str2.Length]);Console.Read();}static void LCS(string str1, string str2){//初始化边界,过滤掉0的情况for (int i = 0; i <= str1.Length; i++)martix[i, 0] = 0;for (int j = 0; j <= str2.Length; j++)martix[0, j] = 0;//填充矩阵for (int i = 1; i <= str1.Length; i++){for (int j = 1; j <= str2.Length; j++){//相等的情况if (str1[i - 1] == str2[j - 1]){martix[i, j] = martix[i - 1, j - 1] + 1;}else{//比较“左边”和“上边“,根据其max来填充if (martix[i - 1, j] >= martix[i, j - 1])martix[i, j] = martix[i - 1, j];elsemartix[i, j] = martix[i, j - 1];}}}}}

图大家可以自己画一画,代码完全是根据上面的公式照搬过来的,长度的问题我们已经解决了,这次要解决输出最长子序列的问题,采用一个标记函数 Flag[i,j],当

①:C[i,j]=C[i-1,j-1]+1 时标记Flag[i,j]="left_up"; (左上方箭头)

②:C[i-1,j]>=C[i,j-1] 时标记Flag[i,j]="left"; (左箭头)

③: C[i-1,j]<C[i,j-1] 时 标记Flag[i,j]="up"; (上箭头)

例如:我输入两个序列X=acgbfhk,Y=cegefkh。

public class Program{static int[,] martix;static string[,] flag;static string str1 = "acgbfhk";static string str2 = "cegefkh";static void Main(string[] args){martix = new int[str1.Length + 1, str2.Length + 1];flag = new string[str1.Length + 1, str2.Length + 1];LCS(str1, str2);//打印子序列SubSequence(str1.Length, str2.Length);Console.Read();}static void LCS(string str1, string str2){//初始化边界,过滤掉0的情况for (int i = 0; i <= str1.Length; i++)martix[i, 0] = 0;for (int j = 0; j <= str2.Length; j++)martix[0, j] = 0;//填充矩阵for (int i = 1; i <= str1.Length; i++){for (int j = 1; j <= str2.Length; j++){//相等的情况if (str1[i - 1] == str2[j - 1]){martix[i, j] = martix[i - 1, j - 1] + 1;flag[i, j] = "left_up";}else{//比较“左边”和“上边“,根据其max来填充if (martix[i - 1, j] >= martix[i, j - 1]){martix[i, j] = martix[i - 1, j];flag[i, j] = "left";}else{martix[i, j] = martix[i, j - 1];flag[i, j] = "up";}}}}}static void SubSequence(int i, int j){if (i == 0 || j == 0)return;if (flag[i, j] == "left_up"){Console.WriteLine("{0}: 当前坐标:({1},{2})", str2[j - 1], i - 1, j - 1);//左前方SubSequence(i - 1, j - 1);}else{if (flag[i, j] == "up"){SubSequence(i, j - 1);}else{SubSequence(i - 1, j);}}}}

由于直接绘图很麻烦,嘿嘿,我就用手机拍了张:

好,我们再输入两个字符串:

static string str1 = "abcbdab";static string str2 = "bdcaba";

通过上面的两张图,我们来分析下它的时间复杂度和空间复杂度。

  • 时间复杂度:构建矩阵我们花费了O(MN)的时间,回溯时我们花费了O(M+N)的时间,两者相加最终我们花费了O(MN)的时间。

  • 空间复杂度:构建矩阵我们花费了O(MN)的空间,标记函数也花费了O(MN)的空间,两者相加最终我们花费了O(MN)的空间。

数据结构与算法专题——第三题 最长公共子序列相关推荐

  1. 经典算法题——最长公共子序列

    ** 解析: ** 此题一共有两个要点: 1.求上述两个最长公共子序列的长度 2.求所有可能出现的最长公共子序列个数,答案可能很大,只要将答案对10^8求余即可 第一个都很好想到,难点在于第二个.下面 ...

  2. c语言最长公共子序列,算法设计与分析/动态规划——最长公共子序列LCS及模板...

    这位大佬写的对理解DP也很有帮助,我就直接摘抄过来了,代码部分来自我做过的题 一,问题描述 给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence).比如字符 ...

  3. 程序员的算法课(6)-最长公共子序列(LCS)

    上一节我们讲了动态规划,我们也知道,动态规划对于子问题重叠的情况特别有效,因为它将子问题的解保存在存储空间中,当需要某个子问题的解时,直接取值即可,从而避免重复计算! 这一节我们来解决一个问题,就是最 ...

  4. 算法复习——动态规划篇之最长公共子序列问题

    算法复习--动态规划篇之最长公共子序列问题 以下内容主要参考中国大学MOOC<算法设计与分析>,墙裂推荐希望入门算法的童鞋学习! 1. 问题背景 子序列:将给定序列中零个或多个元素(如字符 ...

  5. Java获取相同字符串算法题,数据结构与算法专题——第四题 字符串相似度

    这篇我们看看 最长公共子序列 的另一个版本,求字符串相似度(编辑距离),我也说过了,这是一个非常实用的算法,在DNA对比,网页聚类等方面都有用武之地. 一:概念 对于两个字符串 A 和 B,通过基本的 ...

  6. 数据结构与算法专题——第四题 字符串相似度

    这篇我们看看 最长公共子序列 的另一个版本,求字符串相似度(编辑距离),我也说过了,这是一个非常实用的算法,在DNA对比,网页聚类等方面都有用武之地. 一:概念 对于两个字符串 A 和 B,通过基本的 ...

  7. leetcode算法题--最长公共子序列★

    原题链接:https://leetcode-cn.com/problems/longest-common-subsequence/ 动态规划 dp[i][j]表示text1[:i]和text2[:j] ...

  8. 实验三、最长公共子序列(输出所有最长公共子序列)

    实验3.最长公共子序列 问题描述与实验目的: 序列Z=<B,C,D,B>是序列X=<A,B,C,B,D,A,B>的子序列,相应的递增下标序列为<2,3,5,7>. ...

  9. 365天挑战LeetCode1000题——Day 014 每日一题 + 最长公共子序列

    文章目录 1. [我能赢吗](https://leetcode.cn/problems/can-i-win/) 1.1 记忆化搜索+DFS+位运算 2. [最长公共子序列](https://leetc ...

最新文章

  1. K近邻算法(KNN)原理小结
  2. 第七课.Python面向对象(一)
  3. Day45--js基本小结
  4. 【渝粤题库】陕西师范大学210011幼儿园语言教育作业(高起专)
  5. python显示数据长度_python – 获取CSV的长度以显示进度
  6. Excel 二次开发系列(3): 创建Excel二次开发环境
  7. C++ 中map容器
  8. 怎么制作GIF图片并添加文字
  9. python输出列表元素_怎样用一行python打印列表所有元素
  10. Hulu热招|广告智能团队
  11. Sap hana 升级思路
  12. 定义一个形状类(Shape)方法:计算周长,计算面积。子类: 矩形类、圆形类、三角形类...
  13. 这个企业邮箱登陆入口更快捷
  14. “黑产“识别算法(社区检测,相似度,关联关系)
  15. Storm部分:Storm Grouping -- 数据流分组(各种数据分发策略的练习)【Java版纯代码】
  16. (2021牛客多校一)A.Alice and Bob(博弈)
  17. 只网签没备案 房管局能查到吗_房子备案了,但是没有房产证,房管局能查询出来么...
  18. 数据结构题集(严书)查找 常见习题代码
  19. 【免费开放源码】审批类小程序项目实战(IDE介绍篇)
  20. MODIS卫星遥感数据下载处理

热门文章

  1. C#控件绑定数据源方式
  2. 一款好的折线图、饼图、柱形图
  3. mysql 删除5天前 备份_mysql自动备份删除5天前的备份
  4. 小程序 iphone和安卓_如何阻止iPhone和iPad应用程序要求评级
  5. nexus 手动增加_如何使用Google的工厂图像手动升级Nexus设备
  6. mysql ldf文件太大_Linux_数据库清除日志文件(LDF文件过大),清除日志: 复制代码 代码如 - phpStudy...
  7. 浅谈javascript递归(白话版)
  8. VMware Workstation与VMware vSphere的区别
  9. WiFi行业将走向何方?
  10. Maven私服的简单搭建教程(Nexus)