问题:
计算两个字符串的最大公共子序列(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;
}
运行结果: