递归解决最长公共子序列问题(LCS)

 

前言

写在前面,这篇文章是我大一的时候写的,质量比较差,没想到还是被挺多人看到了,有上千的阅读量,说实话有点内疚~~,因为之前写的太差了,看了我文章可能对这个问题更不清楚了。
抱着不能坑别人的想法,我重新编辑了一下文章,希望还是能帮到大家的。

其实用递归解决最长公共子序列是不好的,网上最多的方法应该是用动态规划解这个问题,动态规划解这个问题确实优秀很多,但动态规划理解起来是有难度的,我建议可以先从递归开始理解,然后再使用更好的办法来解决这个问题

思路

那么如何用递归解决LCS(最长公共子序列)呢?
 

这里我就不复述什么是最长公共子序列了,相信大家都知道,不知道的可以百度
 

减而治之


减而治之是我们用递归这个工具来解决LCS问题的主要策略
求解思想:
将问题规模减小,得到子问题,用递归或者迭代的方式求解子问题 

对于两个序列,我们通过自己手动观察计算的时候,其实是经历了以下步骤的
1. 比较两个序列的字符
2. 字符相等进行下一步比较
3. 字符不相等的下一步比较

其实程序也是这样设计的
现在来看一个例子

A序列: didacticA , A[0, m] (m指的是A序列的长度)
B序列: advantA, B[0, n] (n指得是B序列的长度)

 

我们将问题规模减小,从整体的观察整个序列缩小到每个字符进行比较

string a = "didacticA"
string b = "advantA"

我们比较字符,a[0]与b[0]比较

1. 字符不相等, 那我们有两种选择

  • 拿去掉第一位的a,与整个b比较,也就是"idacticA"和"adbantA"比较
  • 拿去掉第一位的b,与整个a比较,也就是"didacticA"和"dvantA"比较

为什么要这么做呢,因为我们不知道这两种选择,哪种会更好,所以我们把两种都计算出来
2. 字符相等,我们减小序列规模,比较下一位
3. 我们不停的重复这个比较的步骤,就能将问题规模一直缩小,最后解决这个问题
(可以自己用手算几步比较的过程,对理解算法有很大的好处)
 

那么用程序来实现我们的想法


1. A[m] == B[n] 则取LCS(m+1, n+1)+1, 因为当前一个字符相同,我们加上1,然后截去这个字符把剩下的两个串继续比较

2. A[m]  != B[n], 这时候我们拆分成两个问题,
截去A当前字符剩下的串与B串比较,写成LCS(m+1, n)
截去B当前字符剩下的串与A串比较,写出LCS(m, n+1)
然后我们返回这两个子问题更大的那一方max(LCS(m, n+1), LCS(m+1, n))

3. 我们的程序什么时候结束呢,当m或者n超出了它们所对应串的长度时

 

 


#include<iostream>
using namespace std;
string a, b;

int LCS(int m, int n) {
  if(m == a.length() || n == b.length()) 
    return 0;
  if(a[m] == b[n]) 
    return LCS(m+1, n+1)+1;
  if(a[m] != b[n]) 
    return max(LCS(m, n+1), LCS(m+1, n));
}

int main() {
  a = "didacticA";
  b = "advantA";
  cout << LCS(0, 0) << endl;
}


这种方式求解LCS效率十分低下,因为会出现大量的重复子问题,所以我们应该考虑使用更好的方法动态规划来解决它

关于动态规划的解法,我就不列出来了,网上有相当多优秀的博客可以参考~

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值