动态规划思想计算最大公共子序列(c++代码)

16 篇文章 0 订阅
16 篇文章 0 订阅

问题:

计算两个字符串的最大公共子序列(longest common substring),
注意是子序列不是子串,子串是要求字符之间是相连的,而序列则只要求是保持前后顺序不变.
比如str1 = "abc"和str2 = "bac"的最长公共子序列为"bc"和“ac".
我们这里要求的是最长公共子序列的长度.

思想:

计算两个数组的最长公共子序列.
设:C[i,j] = LCS(str1[1...i],str2[1...j]),
即C[i,j]表示序列str1[1...i]和str2[1...j]的最长公共子序列的长度,则 C[m,n] = LCS(str1,str2)就是问题的解(长度为m,n).
计算公式为:
if str1[i] == str2[j] then C[i,j] = C[i-1,j-1] +1;
else if str1[i] != str2[j] then C[i,j] = max{C[i-1,j] , C[i,j-1]}.

性能:

时间复杂度O(m*n)。

空间复杂度O(m*n)。

代码:

完整代码已上传我的github,项目名DP,其中还有最大公共子串最长回文子串的实现:https://github.com/yisun03/DP

LCS.h

//
// Created by yis on 2020/7/12.
//

#ifndef DYNAMIC_PROGRAMMING_LCS_H
#define DYNAMIC_PROGRAMMING_LCS_H

#include <string>
#include <vector>

using std::string;
using std::vector;

namespace yis
{
  class LCS
  {
    // 计算两个字符串的最大公共子序列(longest common substring),
    // 注意是子序列不是子串,子串是要求字符之间是相连的,而序列则只要求是保持前后顺序不变.
    // 比如str1 = "abc"和str2 = "bac"的最长公共子序列为"bc"和“ac".
    // 我们这里要求的是最长公共子序列的长度.

  public:
    // 计算两个数中较大的.
    static int max(int a, int b);

    // 计算两个数组的最长公共子序列.
    // 设:C[i,j] = LCS(str1[1...i],str2[1...j]),
    // 即C[i,j]表示序列str1[1...i]和str2[1...j]的最长公共子序列的长度,则 C[m,n] = LCS(str1,str2)就是问题的解(长度为m,n).
    // 计算公式为:
    // if str1[i] == str2[j] then C[i,j] = C[i-1,j-1] +1;
    // else if str1[i] != str2[j] then C[i,j] = max{C[i-1,j] , C[i,j-1]}.
    static int lcs(string& str1, string& str2,int m,int n);
  };
}


#endif //DYNAMIC_PROGRAMMING_LCS_H

 LCS.cpp

//
// Created by yis on 2020/7/12.
//

#include "LCS.h"

namespace yis
{
  int LCS::max(int a, int b)
  {
    return a>b ? a : b;
  }

  int LCS::lcs(string &str1, string &str2, int m, int n)
  {
    // 维护一个动态规划(m+1)*(n+1)的动态规划表(两个字符长为str1[0...m-1] 和 str2[0...n-1]).
    // 用vector数据结构实现这张表.
    vector<vector<int>> dp_table(m+1,vector<int>(n+1));
    for(int i = 0; i < m+1; ++i)
    {
      for(int j = 0; j < n+1; ++j)
      {
        // 第一行和第一列为0,str从dp_table的第1行/列开始对应.
        if(i == 0 || j == 0)
        {
          dp_table[i][j] = 0;
        }
        else if (str1[i-1] == str2[j-1])
        {
          // 字符串的k对应dp_table的k+1.
          dp_table[i][j] = dp_table[i-1][j-1] + 1;
        }
        else
        {
          dp_table[i][j] = max(dp_table[i-1][j] , dp_table[i][j-1]);
        }
      }
    }
    return dp_table[m][n];
  }
}

main.cpp

#include <iostream>
#include "LCS.h"

int main()
{
  string str1 = "acdebfgzyx";
  string str2 = "ebcdayxgfz";

  std::cout << "str1 和str2的最长公共子序列为:\n " << yis::LCS::lcs(str1, str2, str1.length(), str2.length());
  std::cout << std::endl;
  return 0;
}

运行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yisun03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值