最长公共子序列是经典的动态规划问题,在很多书籍和文章中都有介绍,这里对这一经典算法进行回顾并对两个follow up questions进行总结和分析。

1. 回顾LCS(longest common subsequence)解法,求LCS长度

典型的双序列动态规划问题,dp[i][j]表示第一个序列前i项与第二个序列的前j项....

所以对应此题,dp[i][j]表示序列s1的前i项与序列s2的前j项的最长公共子序列。

得到如下递推关系式:

dp[i][j] = dp[i - 1][j - 1] + 1                  if s1[i - 1] == s2[j - 1]

    = max(dp[i - 1][j], dp[i][j - 1])   if s1[i - 1] != s2[j - 1] ;

对dp[0][j] j = 0,1,... 于 dp[i][0] i = 0,1...初始化,根据递推关系式则可以得到结果。

程序:

 1 int longsetCommonSubsequence(const string& s1, const string& s2) {
 2     int sz1 = s1.size(), sz2 = s2.size();
 3     int dp[sz1 + 1][sz2 + 1];
 4     for (int i = 0; i <= sz1; ++i) {
 5         dp[i][0] = 0;
 6     }
 7     for (int i = 0; i <= sz2; ++i) {
 8         dp[0][i] = 0;
 9     }
10     for (int i = 1; i <= sz1; ++i) {
11         for (int j = 1; j <= sz2; ++j) {
12             if (s1[i] == s2[j]) {
13                 dp[i][j] = dp[i - 1][j - 1] + 1;
14             }
15             else {
16                 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
17             }
18         }
19     }
20     return dp[sz1][sz2];
21 }

2. 如何得到最长公共子序列?

其实本质上,当有了dp数组时,即得到了行程LCS的所有信息。

我们考察一个例子,求s1 = "ABCBDAB" 和 s2 = "BDCABA"的 LCS, 得到其dp数组如下。(图片来自邹博)

我们可以看到,当dp数组被填充完毕后。反向探索LCS的形成过程,结合递推公式可知,每一个添加到LCS中的字符是在s1[i - 1] == s2[j - 1]的情况下加入的。

也就是上述图中向同时向左上方的那些步骤,而对应s1[i - 1] != s2[j - 1]的那些步骤,则选择其dp值大的(原因在于生成的时候路径就是选的max)进行前进即可。

所以总结寻找LCS的算法步骤即:

如果s1[i - 1] == s2[j - 1],将其push_back到结果中;

如果s1[i - 1] != s2[j - 1],选择dp[i - 1][j]与dp[i][j - 1]中大的更新i--或j--。

直到i或者j达到0后,翻转上述结果即可。

代码:

 1 string longsetCommonSubsequence2(const string& s1, const string& s2) {
 2     int sz1 = s1.size(), sz2 = s2.size();
 3     int dp[sz1 + 1][sz2 + 1];
 4     for (int i = 0; i <= sz1; ++i) {
 5         dp[i][0] = 0;
 6     }
 7     for (int i = 0; i <= sz2; ++i) {
 8         dp[0][i] = 0;
 9     }
10     for (int i = 1; i <= sz1; ++i) {
11         for (int j = 1; j <= sz2; ++j) {
12             if (s1[i - 1] == s2[j - 1]) {
13                 dp[i][j] = dp[i - 1][j - 1] + 1;
14             }
15             else {
16                 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
17             }
18         }
19     }
20     string result;
21     int i = sz1, j = sz2;
22     while (i > 0 && j > 0) {
23         if (s1[i - 1] == s2[j - 1]) { //对应的是s1[i - 1], s2[j - 1]
24             result.push_back(s1[i - 1]);
25             i--;
26             j--;
27         }
28         else {
29             if (dp[i - 1][j] > dp[i][j - 1]) {
30                 i--;
31             }
32             else {
33                 j--;
34             }
35         }
36     }
37     reverse(result.begin(), result.end());
38     return result;
39 }

3. 有多组解都要输出怎么办?

首先考虑什么时候会有多组解?还考虑上述图示可以发现,当s1[i - 1] != s2[j -1]但 dp[i - 1][j] == dp[i][j - 1]时,i--, j--均可。所以可能产生多组解。

想要输出所有解的想法其实也很直观,就是DFS,把所有情况都遍历一遍,并进行去重(代码中find语句),把不同路径的相同解删除掉,即可得到所有解。

一段完整的,带简单测试的程序如下:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <algorithm>
 5 using namespace std;
 6 vector<int> temp;
 7 vector<vector<int>> resultIndex;
 8 void dfs(const vector<vector<int>>& dp, const string& s1, const string& s2, int i, int j) {
 9     if (i == 0 || j == 0) {
10         if (find(resultIndex.begin(), resultIndex.end(), temp) == resultIndex.end()) {
11             resultIndex.push_back(temp);
12         }
13         return ;
14     }
15     if (s1[i - 1] == s2[j - 1]) {
16         temp.push_back(i - 1);
17         dfs(dp, s1, s2, i - 1, j - 1);
18     }
19     else {
20         if (dp[i - 1][j] > dp[i][j - 1]) {
21             dfs(dp,s1,s2,i - 1, j);
22         }
23         else if (dp[i - 1][j] < dp[i][j - 1]) {
24             dfs(dp, s1, s2, i, j - 1);
25         }
26         else {
27             vector<int> temp2 = temp;
28             dfs(dp,s1,s2,i - 1, j);
29             temp = temp2;
30             dfs(dp,s1,s2,i, j - 1);
31         }
32     }
33     return;
34 }
35
36 vector<string> longsetCommonSubsequence2(const string& s1, const string& s2) {
37     int sz1 = s1.size(), sz2 = s2.size();
38     vector<vector<int>> dp(sz1 + 1,vector<int>(sz2 + 1));
39     for (int i = 0; i < sz1; ++i) {
40         dp[i][0] = 0;
41     }
42     for (int i = 0; i < sz2; ++i) {
43         dp[0][i] = 0;
44     }
45     for (int i = 1; i <= sz1; ++i) {
46         for (int j = 1; j <= sz2; ++j) {
47             if (s1[i - 1] == s2[j - 1]) {
48                 dp[i][j] = dp[i - 1][j - 1] + 1;
49             }
50             else {
51                 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
52             }
53         }
54     }
55     vector<string> result;
56     dfs(dp,s1,s2,sz1,sz2);
57     for (int i = 0; i < resultIndex.size(); ++i) {
58         string s;
59         result.push_back(s);
60         for (int j = resultIndex[i].size() - 1; j >= 0; --j) {
61             result[i].push_back(s1[resultIndex[i][j]]);
62         }
63     }
64     return result;
65
66 }
67
68 int main() {
69     string s1 = "ABCBDAB", s2 = "BDCABA";
70     vector<string> result = longsetCommonSubsequence2(s1,s2);
71     for (int i = 0; i < result.size(); ++i) {
72         cout << result[i] << endl;
73     }
74     return 0;
75 }

转载于:https://www.cnblogs.com/wangxiaobao/p/5982901.html

最长公共子序列及其引申问题相关推荐

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

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

  2. 【动态规划】最长公共子序列与最长公共子串

    1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...

  3. POJ 3080 多个串最长公共子序列

    求多个串最长公共子序列,字典序最小输出.枚举剪枝+kmp.比较简单,我用find直接查找16ms #include<iostream> #include<string> #in ...

  4. java实现最长连续子序列_最长公共子序列 ||

    问题:在 前一篇文章 最长公共子序列 | 的基础上要求将所有的最长公共子序列打印出来,因为最长公共子序列可能不只一种. 难点:输出一个最长公共子序列并不难,难点在于输出所有的最长公共子序列,我们需要在 ...

  5. 动态规划—最长公共子序列问题 HDU-1159 Common Subsequence

    动态规划-最长公共子序列问题 Common Subsequence [ HDU - 1159 ] A subsequence of a given sequence is the given sequ ...

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

    作者 |  labuladong 来源 | labuladong(ID:labuladong) [导读]最长公共子序列(Longest Common Subsequence,简称 LCS)是一道非常经 ...

  7. 模板 - 最长上升子序列与最长公共子序列

    整理的算法模板合集: ACM模板 目录 1.最长上升子序列(LIS) 1.1树状数组优化O(nlogn)O(nlogn)O(nlogn) 2.最长公共子序列(LCS) 2.1转换成LIS优化O(nlo ...

  8. 最长公共子序列(LCS)问题算法详解+例题(转换成LIS,优化为O(nlogn),看不懂你来打我)

    目录 最长公共子序列(LCS)问题 1.朴素做法 O(n2)O(n^2)O(n2) 2.转换成LIS优化O(nlogn)O(nlogn)O(nlogn) 3.P2758 编辑距离 最长公共子序列(LC ...

  9. LCS最长公共子序列和LIS最长上升子序列——例题剖析

    一.LCS最长公共子序列 最长公共子序列(LCS)问题算法详解+例题(转换成LIS,优化为O(nlogn),看不懂你来打我) longest comment subsequence 模板题 longe ...

最新文章

  1. 网站基本建设必备窍门了解一下!
  2. 1.2.3 OSI参考模型(2)
  3. ms+sql迁移到mysql_mssql数据迁移到mysql
  4. 运行Vue在ASP.NET Core应用程序并部署在IIS上
  5. java 链接mysql 产生500W数据模拟生成环境
  6. 51单片机智能小车循迹完整程序_电气与信息工程学院双创协会开展循迹小车培训...
  7. django-orm-查询基本操作
  8. 自定义可扩展叠加头部的下拉控件
  9. webdriver原理_(最新版)如何正确移除 Pyppeteer 中的window.navigator.webdriver
  10. iOS开发:2分钟快速集成支付宝快捷支付
  11. 北大核刊最新版2020目录_重磅!最新版CSSCI 来源期刊目录(2019-2020)
  12. su必备插件_会了这10个插件,Sketchup也能玩飞起来!
  13. HTML期末学生大作业-婚庆网页作业html+css+javascript (企业网站源码)
  14. Android使用Volley框架显示“Cleartext HTTP traffic to XXX not permitted”的解决方法
  15. 开发一流Android SDK
  16. 编程序,输出1/3-3/5+5/7-7/9…+19/21的结果
  17. pyqt5 图片随窗口变化等比例缩放
  18. springboot智慧餐厅点餐餐桌预订系统设计与实现
  19. Intel桌面CPU系列
  20. PXC高可用集群(MySQL)

热门文章

  1. Android企业级程序完全退出的解决方案
  2. 一个程序员的创业失败教训
  3. J2EE WEBWORK FRAMEWORK安全隐患
  4. spdk/dpdk 编译相关问题汇总
  5. 微信小程序 悬浮按钮
  6. 在 PowerPoint 2016 中嵌入网页
  7. volatile的应用
  8. 将DataTable 数据插入 SQL SERVER 数据库
  9. 关于软件版本命名的方法
  10. 搭建Servlet在线视频