问题描述
最长公共子序列:实现求解LCS的动态规划算法,如:输入两个字符串A和B,输出A相对于B的重复率,并标记出字符串A中相对于B串的LCS
问题分析
分析
若设A=(a0,a1,…,am-1)(含m个字符),B=(b0,b1,…,bn-1)(含n个字符),设Z=(z0,z1,…,zk-1)(含k个字符)为它们的最长公共子序列。
-
如果am-1=bn-1,则zk-1=am-1=bn-1,且(z0,z1,…,zk-2)是(a0,a1,…,am-2)和(b0,b1,…,bn-2)的一个最长公共子序列;
-
如果am-1≠bn-1且zk-1≠am-1,则(z0,z1,…,zk-1)是(a0,a1,…,am-2)和(b0,b1,…,bn-1)的一个最长公共子序列;
-
如果am-1≠bn-1且zk-1≠bn-1,则(z0,z1,…,zk-1)是(a0,a1,…,am-1)和(b0,b1,…,bn-2)的一个最长公共子序列。
对应的状态转移方程:
dp[i][j]=0 i=0或j=0,边界条件 dp[i][j]=dp[i-1][j-1]+1 a[i-1]=b[j-1] dp[i][j]=MAX(dp[i][j-1],dp[i-1][j]) a[i-1]≠b[j-1]
由dp求出LCS:
-
当dp[i][j] ≠ dp[i][j-1](左边)并且dp[i][j] ≠ dp[i-1][j](上方)值时: a[i-1]=b[j-1] 将a[i-1]添加到LCS中,i--,j--
-
dp[i][j]=dp[i][j-1]:与左边相等: j--
-
dp[i][j]=dp[i-1][j]:与上方相等: i-- 与左边、上方都不相等:a[i-1]或者b[j-1]属于LCS :
代码实现
#include <iostream>
#include<vector>
using namespace std;
#define MAX 51
string strina, strinb;
int m, n;
int dp[MAX][MAX];
vector<char> subs;
void LCSLength() {
int i, j;
for (i = 0; i <= m; i++) {
dp[i][0] = 0;
}
for (j = 0; j <= n; j++) {
dp[0][j] = 0;
}
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (strina[i - 1] == strinb[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
void BuildSubs() {
int k = dp[m][n];
int i = m;
int j = n;
while (k > 0) {
if (dp[i][j] == dp[i - 1][j])
i--;
else if (dp[i][j] == dp[i][j - 1])
j--;
else {
subs.push_back(strina[i - 1]);
//subs[i - 1] = strina[i - 1];
i--;
j--;
k--;
}
}
}
int main()
{
cout << "请输入第一个字符串A:" << endl;
cin >> strina;
cout << "请输入第二个字符串B:" << endl;
cin >> strinb;
m = strina.length();
n = strinb.length();
LCSLength();
BuildSubs();
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
cout << dp[i][j] << " ";
}
cout << endl;
}
double repetition = subs.size() / sizeof(char);
cout << "A相对于B的重复率:" << repetition / n << endl;
cout << "重复序列:";
for (int i = subs.size()-1; i >= 0; i--) {
cout << subs[i];
}
cout << endl;
}