本系列算法实现都是在学习数据结构(C++语言版),清华大学邓俊辉教授编,听邓老师edX网课过程中自己实现的例子。
参考链接
问题:
(1)找到LCS的长度
(2)找到其中一个LCS序列
(3)找出所有LCS序列
解决: 以下代码实现解决以上三个问题,第一个链接主要解决第三个问题,第二个链接解决第一二两个问题
1 LCS算法接口
我把它封装成了一个类,接口如下
#include <iostream>
#include <string>
#include <eigen3/Eigen/Eigen>
#include <set>
class LongestCommonSubsequence
{
public:
LongestCommonSubsequence(const std::string& _s1, const std::string& _s2);
~LongestCommonSubsequence();
void get_lcs_one(std::string& str);
void get_lcs_set(std::set<std::string>& strset);
int get_lcs_length();
void print_lcs_set();
private:
std::string s1, s2; //str
Eigen::MatrixXi len_mat;//table matrix
std::set<std::string> lcs_set;//lcs set
void lcs_creat_table();
void lcs_back_traversal(int i, int j, std::string str);
};
2 找到LCS的长度
LCS的长度为所建立的矩阵的右下角元素
int LongestCommonSubsequence::get_lcs_length()
{
return len_mat(s1.length(), s2.length());
}
建立矩阵如下
//! class LongestCommonSubsequence private interface BEGIN
void LongestCommonSubsequence::lcs_creat_table()
{
for (auto i = 1; i <= s1.length(); i++)
{
for (auto j = 1; j <= s2.length(); j++)
{
//equal,OBLIQUE_UP_LEFT
if (s1[i - 1] == s2[j - 1])
len_mat(i, j) = len_mat(i - 1, j - 1) + 1;
else
len_mat(i, j) = std::max(len_mat(i - 1, j), len_mat(i, j - 1));//UP
}
}
}
3 找到其中一个LCS序列
这个问题和第三个类似,主要区别在于如果想要找到全部的子序列,需要从矩阵的右下角回溯到左上角,需要用到递归。而只找一个子序列只是其中的一个特殊情况,所有可以直接注释一部分找到全部子序列的代码。
void LongestCommonSubsequence::get_lcs_one(std::string& str)
{
auto i = s1.length();
auto j = s2.length();
while (i && j)
{
if (s1[i - 1] == s2[j - 1])
{
str = s1[i - 1] + str;
i--;
j--;
}
else
{
if (len_mat(i - 1, j) >= len_mat(i, j - 1))
i--;
else //if (len_mat(i - 1, j) < len_mat(i, j - 1))
j--;
// else
// {
// lcs_back_traversal(i - 1, j, str);
// lcs_back_traversal(i, j - 1, str);
// return;
// }
}
}
}
4 找出所有LCS序列
这里之所以用< set >来存储所有的lcs,是考虑不重复,如果重复问题不在乎的话,使用< vector >也可以。
void LongestCommonSubsequence::lcs_back_traversal(int i, int j, std::string str)
{
while (i && j)
{
if (s1[i - 1] == s2[j - 1])
{
str = s1[i - 1] + str;//reverse str
i--;
j--;
}
else
{
if (len_mat(i - 1, j) > len_mat(i, j - 1))
i--;
else if (len_mat(i - 1, j) < len_mat(i, j - 1))
j--;
else
{
//recursion
lcs_back_traversal(i - 1, j, str);
lcs_back_traversal(i, j - 1, str);
return;
}
}
}
lcs_set.insert(str);
}//! class LongestCommonSubsequence private interface END
5 实现代码附录
#include <iostream>
#include <string>
#include <set>
#include <eigen3/Eigen/Eigen>
#include "lcs.h"
using namespace std;
//! class LongestCommonSubsequence public interface BEGIN
LongestCommonSubsequence::LongestCommonSubsequence(const std::string& _s1, const std::string& _s2): s1(_s1), s2(_s2)
{
len_mat = Eigen::MatrixXi::Zero(s1.length() + 1, s2.length() + 1);
//crreat table
lcs_creat_table();
//back traversal
std::string lcs_str("");
lcs_back_traversal(s1.length(), s2.length(), lcs_str);
}
LongestCommonSubsequence::~LongestCommonSubsequence() {}
void LongestCommonSubsequence::get_lcs_one(std::string& str)
{
auto i = s1.length();
auto j = s2.length();
while (i && j)
{
if (s1[i - 1] == s2[j - 1])
{
str = s1[i - 1] + str;
i--;
j--;
}
else
{
if (len_mat(i - 1, j) >= len_mat(i, j - 1))
i--;
else //if (len_mat(i - 1, j) < len_mat(i, j - 1))
j--;
// else
// {
// lcs_back_traversal(i - 1, j, str);
// lcs_back_traversal(i, j - 1, str);
// return;
// }
}
}
}
void LongestCommonSubsequence::get_lcs_set(std::set<std::string>& strset)
{
strset = lcs_set;
}
int LongestCommonSubsequence::get_lcs_length()
{
return len_mat(s1.length(), s2.length());
}
void LongestCommonSubsequence::print_lcs_set()
{
std::cout << std::endl << "-----lcs set-----" << std::endl;
std::cout << "length of lcs : " << get_lcs_length() << std::endl;
std::cout << "size of lcs set: " << lcs_set.size() << std::endl;
int j = 0;
for (auto i = lcs_set.begin(); i != lcs_set.end(); i++)
{
std::cout << j++ << " " << *i << std::endl;
}
}//! class LongestCommonSubsequence public interface END
//! class LongestCommonSubsequence private interface BEGIN
void LongestCommonSubsequence::lcs_creat_table()
{
for (auto i = 1; i <= s1.length(); i++)
{
for (auto j = 1; j <= s2.length(); j++)
{
//UP_LEFT
if (s1[i - 1] == s2[j - 1])
len_mat(i, j) = len_mat(i - 1, j - 1) + 1;
else
len_mat(i, j) = std::max(len_mat(i - 1, j), len_mat(i, j - 1));//UP
}
}
}
void LongestCommonSubsequence::lcs_back_traversal(int i, int j, std::string str)
{
while (i && j)
{
if (s1[i - 1] == s2[j - 1])
{
str = s1[i - 1] + str;
i--;
j--;
}
else
{
if (len_mat(i - 1, j) > len_mat(i, j - 1))
i--;
else if (len_mat(i - 1, j) < len_mat(i, j - 1))
j--;
else
{
lcs_back_traversal(i - 1, j, str);
lcs_back_traversal(i, j - 1, str);
return;
}
}
}
lcs_set.insert(str);
}//! class LongestCommonSubsequence private interface END
6 测试用例
int main(int argc, char const *argv[])
{
string s1("ABCBDAB");//6
string s2("BDCABA");//5
std::set<std::string> lcs_set;
LongestCommonSubsequence lcs(s1, s2);
lcs.get_lcs_set(lcs_set);
lcs.print_lcs_set();
std::string str;
lcs.get_lcs_one(str);
cout << str << endl;
return 0;
}