一、问题描述
有两个字符串,求二者的最长公共子序列。
最长公共子序列:不必连续但必须有序的子列(与子串区分,子串是连续的)

二:解决方法
第一种方法:穷举法 ,就是一个一个的对比,但这个方法的时间复杂度为O(2^n),故而不多做赘述。
第二种方法:分而治之+动态规划法。

三:动态规划
关于动态规划法我的理解是增加空间代价来减少时间代价,这个方法常用于寻找最优解。
分而治之类似,通过某种手段将一个大问题分解成为若干个小问题,再分解成更小的问题,但这样的话会导致重复计算,就是小问题与小问题之间有相同的地方,从而导致时间代价大大提高,而动态规划法通过将每个小问题的解求出来,放到一个表中,要用的时候拿出来,这样来减少运算量,减少时间代价,提高空间代价,但相比于超高的时间代价而言这是个更优解。
四:问题分析
我们用分而治之的方法来对这个问题进行分析。有如下字符串:

Xi<x1,x2,x3,…,xn>
Yi<y1,y2,y3,…,ym>

1,若xn=ym
这种情况下,最长子序列的最后一个字符一定是xn(ym),所以我们可以将同时去掉这两个字符,之后再将改动后的字符串的最长子序列求出来加上这个字符即可。
2,若xn!=ym
这种情况下我们可以分为两种情况,一种是去掉Xi的最后一个字符,另一个是去掉Yi的最后一个字符,分别对这两个改动后的字符串求最长子序列。
总体来说如下:

从这两种情况来看我们需要用到递归的方法,一次一次的将问题拆解,这也就是分而治之,同时这也暴露的一个问题,也就是我上面所提到的,重复计算,时间代价大大提高,故而在此基础上我们要结合动态规划的方法。
既然我们要将大问题分为小问题,那么为什么不先把小问题的解提前算好呢?
为了先算好这个问题,我们得逆向考虑。不是从尾巴开始一刀一刀砍,而是从头开始一个一个算。这里有两个字符串:

利用这个表我们可以得知每个子问题的解。
那么这个表是怎么来的呢?
我们暂且忽略第一排0和第一列0,这个是用代码实现的时候用到的,也可以理解是两个空字符串。
我们分析第二排,单拎出这个A,和上面这个字符串一个一个对比,A和B存在零个相同的字符,故表格中的数为0,往后移,A和BD有0个相同的字符,故为0,往后移,A和BDC有0个,往后移,A和BDCA有一个相同的字符,故为1,以此推类,A分析完分析AB,以此推类。最终得到这样一个表格。
但这样还不够,我们注意到表中的箭头号,其中左上箭头表示连个字符串的最后一个字符相同,故同时去掉这个箭头就指向对角线,而左箭头和上箭头则指向二者不同从而分为两个子问题,两个子问题中的更大解,这个就联系到了我们上面分而治之所分析的。
综上我们将这个表制作了出来,有子问题的解,也有方向,从而我们就不需要用递归来找了最优解了,而是通过这个表来找个最优解,从这个表中我们找到一条直通0的通路,将这条通路的每个左上箭头所代表的字符连接成串就是我们要找的最长公共子序列,到这里我们的分析结束了,代码方面也可以轻松的写出来。
五、代码实现
代码方面我们只需要做出一个表就行,在这里我用到的是结构体数组,结构体中包含了解和方向。

#include <iostream>
#include <string>
using namespace std;
struct attribute
{int derection;int sum;
};
int maxoftwo(int a1, int a2)
{if (a1 > a2)return a1;return a2;
}
void LCS(string s1, string s2, int lth1, int lth2)
{attribute att[lth1 + 1][lth2 + 1];//加一是因为还有一排0和一列0,这里读者可以结合代码想一想为什么for (int i = 0; i < lth1 + 1; i++){for (int j = 0; j < lth2 + 1; j++){att[i][j].derection = 1;att[i][j].sum = 0;}}for (int i = 0; i < lth1; i++){for (int j = 0; j < lth2; j++){if (s1[i] == s2[j]){att[i + 1][j + 1].sum = att[i][j].sum + 1;att[i + 1][j + 1].derection = 0;}if (s1[i] != s2[j]){att[i + 1][j + 1].sum = maxoftwo(att[i][j + 1].sum, att[i + 1][j].sum);att[i + 1][j + 1].derection = att[i][j + 1].sum > att[i + 1][j].sum ? -1 : 1;}}}char s[att[lth1][lth2].sum];int k = att[lth1][lth2].sum - 1;//最长子序列数组int i = lth1, j = lth2;while (att[i][j].sum != 0){if (att[i][j].derection == 0){s[k] = s1[i - 1];i--;j--;k--;}if (att[i][j].derection == 1)j--;if (att[i][j].derection == -1)i--;}for (int i = 0; i < att[lth1][lth2].sum; i++)cout << s[i];cout << endl;
}
int main()
{string s1, s2;cin >> s1 >> s2;string s;int length1 = s1.length();int length2 = s2.length();LCS(s1, s2, length1, length2);
}

至此,这个问题我们就解决了,关于直接用递归的方法我也不太想去写了,太麻烦了,效率又低,算了算了,读者若有兴趣可以自己写写,可以的话可以私信发给我康康。
注:本篇文章的图片来自于:
程序员编程艺术第十一章:最长公共子序列(LCS)问题

LCS(longest common sequence)算法的实现(十分详细)相关推荐

  1. LCS算法(Longest Common Sequence)

    LCS算法 Longest Common Sequence 假设存在两个字符串序列 X 和 Y X={x1,x2,...,xn}X = \{x_1, x_2, ..., x_n\} X={x1​,x2 ...

  2. 最长公共子串LCS (Longest Common Subsequence) 算法

    三个方法都有所借鉴,但代码部分是自己试着写出来的,虽然最后的运行结果都是正确的,但此过程中难免会有考虑不周全的地方,如发现代码某些地方有误,欢迎指正.同时有新的想法,也可以提出! 采用顺序结构存储串, ...

  3. SPOJ LCS Longest Common Substring

    A string is finite sequence of characters over a non-empty finite set Σ. In this problem, Σ is the s ...

  4. SPOJ - LCS Longest Common Substring(后缀自动机)

    题目链接:点击查看 题目大意:给出两个字符串,求出其最长公共子串的长度 题目分析:可以对第一个字符串建立SAM,然后令第二个字符串在建好的SAM上跑就好了,如果遇到不能跑的点,就往上折返直到找到可以继 ...

  5. POJ 2774 Long Long Message SP1811 LCS - Longest Common Substring 题解

    POJ:题目传送门 洛谷:题目传送门 题目大意: 求两个字符串的最长公共子串长度. 题解 后缀数组入门题,将两个字符串接在一起,中间用一个字符集以外的字符隔开,然后求出 h e i g h t hei ...

  6. matlab中注水算法的实现和原理

    注水算法的实现及详细原理 注水算法的公式 算法的相关公式用图片展示 注水算法的公式 这公式怎么编辑啊,有点复杂啊 算法的相关公式用图片展示 前段时间学习使用注水算法,CSDN上分享的代码有很多,大同小 ...

  7. UVA10405 Longest Common Subsequence【LCS+DP】

    Given two sequences of characters, print the length of the longest common subsequence of both sequen ...

  8. 最长公共子序列(LCS)问题 Longest Common Subsequence 与最长公告字串 longest common substr...

    问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X="x0,x1,-,xm-1",序列Y=& ...

  9. C++shortest common supersequence最短公共超序列算法的实现(附完整源码)

    C++shortest common supersequence最短公共超序列算法的实现 C++shortest common supersequence最短公共超序列算法的实现的完整源码(定义,实现 ...

最新文章

  1. zoj 3627(贪心)
  2. Ruby版本管理(RVM)
  3. C++中volatile关键字
  4. roll() java_Java Calendar roll()用法及代码示例
  5. loaded the ViewController nib but the view outlet was not set. 处理方式
  6. 前端文件path路由:基于base引用
  7. es6 Proxy 简介
  8. arm s32440a系统时钟设置
  9. C++标准转换运算符:static_cast
  10. VS2015sql本地服务器为空,详解VS2015自带LocalDB数据库用法实例
  11. android 渐变歌词,Android UI之自定义——最简单的仿QQ音乐歌词颜色渐变
  12. 达朴汇联CEO张焱:从隐私计算出发,共建Web3.0
  13. 关于三栏式布局的几种方式
  14. 2013年c语言课后作业答案,C语言课后作业答案.pdf
  15. PCB学习(一)——立创EDA边框设置
  16. Bluetooth tethering不能用问题
  17. 智安网络丨德勤发布2021九大技术趋势,零信任安全成为主流
  18. Oracle 数据库表空间的管理
  19. 论文阅读——A Comprehensive Study on Deep Learning-Based 3D Hand Pose Estimation Methods综述阅读2
  20. 怒刷python作业(西北工业大学cpSkill平台)

热门文章

  1. C语言C++编程学习:排序原理分析
  2. php图片生成加密pdf文件,php生成PDF格式文件并且加密
  3. Python中动态编译函数compile(source, filename, mode, ......)参数filename的作用是什么?...
  4. 算法竞赛入门经典 习题6-14
  5. android chrome无法运行,Android 测试 Chrome 浏览器能正常启动 Chrome 浏览器,但是不能进行操作,求大神!!...
  6. GitHub:图像分类/目标检测资料
  7. 微信小程序学习笔记(三)——两名片小程序实例
  8. IDEA将项目打包成jar包
  9. python爬虫—Requests
  10. java 1 2 等于_java 判断语句中一个等于号和两个等于号的区别是什么?