求两个字符串的最长公共子序列——程序设计艺术与方法实验四 动态规划算法的实现
(1) 求两个字符串的最长公共子序列。 - 151 - X 的一个子序列是相应于 X 下标序列{1, 2, …, m}的一个子序列,求解两个序列的所有子序列中长度最大的,例如输入:pear, peach 输出:pea。
步骤:
- 确定子问题
- 确定状态转移方程
res[i][j]表示text1截止到第i个字符串,text2截止到第j个字符串的最长公共子串
text1[i]=text2[j],res[i][j]=原结果 + 1
text1[i]!=text2[j],res[i][j]=原结果
原结果? res[i-1][j],res[i-1][j-1] √,res[i][j-1]
text1[i] = text2[j],res[i][j] = res[i-1][j-1] + 1
text1[i] != text2[j],res[i][j] = max{res[i-1][j],res[i][j-1]}
因此,此题在计算出一个子问题的答案后, 将其存储在一个表中。此后调用将检查表。
我们先通过状态转移方程填表,并返回表的最右下角元素(最长公共子序列长度)
之后我们根据递归公式构建了上表,我们将从最后一个元素res[m][n]倒推出text1和text2的LCS。
如果遇到text 1[i] == text 2[j],记录此时的公共值,此时的值来自于res[i-1][j-1]
如果遇到text 1[i] != text 2[j] ,此时的值来自于max{res[i-1][j] ,res[i][j-1] }即他两的最大值。
如果遇到text 1[i] != text 2[j] ,且res[i-1][j] = res[i][j-1] 这种存在分支的情况,这里请都选择一个方向(之后遇到这样的情况,也选择相同的方向)。两种方向都选进行递归。
代码:
#include<bits/stdc++.h>
using namespace std;
/*
给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度
一个字符串的子序列是指这样一个新的字符串:
它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串
eg. text1 = "abcbcba", text2 = "abcba" 公共子串abcba
步骤:
1.确定子问题
2.确定状态转移方程
res[i][j]表示text1截止到第i个字符串,text2截止到第j个字符串的最长公共子串
text1[i]=text2[j],res[i][j]=原结果 + 1
text1[i]!=text2[j],res[i][j]=原结果
原结果? res[i-1][j],res[i-1][j-1] √,res[i][j-1]
text1[i] = text2[j],res[i][j] = res[i-1][j-1] + 1
text1[i] != text2[j],res[i][j] = max{res[i-1][j],res[i][j-1]}
*/
string test1;
string test2;
vector<vector<int> > res; // 动态规划表
set<string> lcs; // set保存所有的LCS
int lcs_length(int m, int n){
// 表的大小为(m+1)*(n+1)
res = vector<vector<int> >(m + 1, vector<int>(n + 1));
for (int i = 0; i < m + 1; ++i){
for (int j = 0; j < n + 1; ++j){
// 第一行和第一列置0
if (i == 0 || j == 0)
res[i][j] = 0;
else if (test1[i - 1] == test2[j - 1])
res[i][j] = res[i - 1][j - 1] + 1;
else
res[i][j] = max(res[i - 1][j], res[i][j - 1]);
}
}
return res[m][n];
}
void lcs_print(int i, int j, string lcs_str){
while (i > 0 && j > 0){
if (test1[i - 1] == test2[j - 1]){
lcs_str.push_back(test1[i - 1]);
i--;
j--;
}
else{
if (res[i - 1][j] > res[i][j - 1])
i--;
else if (res[i - 1][j] < res[i][j - 1])
j--;
else{
lcs_print(i - 1, j, lcs_str);
lcs_print(i, j - 1, lcs_str);
return;
}
}
}
reverse(lcs_str.begin(), lcs_str.end());
lcs.insert(lcs_str);
}
int main(){
while(cin >> test1){
cin>> test2;
int m = test1.length();
int n = test2.length();
int length = lcs_length(m, n);
cout << "最长公共子串的长度是:" << length << endl;
string str;
lcs_print(m, n, str);
set<string>::iterator it = lcs.begin();
cout << "最长公共子串是:"<<endl ;
for (; it != lcs.end(); it++)
cout << *it << endl;
lcs.clear();
res.clear();
}
return 0;
}
运行结果: