本文求解一道LCS算法题,主要难点是如何回溯。
经典的LCS(Longest Common Sequence)问题,这里一样只放代码不讲解思路因为网上有教程更好,LCS问题的难点在于如何通过dp数组来进行回溯,进行长度计算应该是最简单的dp之一了,关键在于回溯把结果显示出来。
1、无需回溯版:
//求<1,0,0,1,0,1,0,1>和<0,1,0, 1, 1,0, 1, 1,0>的一个最长公共子序列
#include<bits/stdc++.h>
using namespace std;
string str1 = "10010101";
string str2 = "010110110";
int dp[50][50];
int main() {
for(int i=1; i<=str1.length(); i++) {
for(int j=1; j<=str2.length(); j++) {
if(str1[i] == str2[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
}
else {
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
cout<<dp[str1.length()][str2.length()]<<endl;
return 0;
}
2、增加回溯:我们需要新开一个记忆数组来记忆我们的dp是从左上角移下来的还是从上方还是左边移过来的,用递归实现这个回溯过程。
回溯过程如下图所示:
实现代码
//求<1,0,0,1,0,1,0,1>和<0,1,0, 1, 1,0, 1, 1,0>的一个最长公共子序列
#include<bits/stdc++.h>
using namespace std;
string str1 = "10010101";
string str2 = "010110110";
int dp[50][50];
int mem[50][50];//记忆表
void printLCS(int left,int right) {
if(left == 0 || right == 0) return;
if(mem[left][right] == 0) {
printLCS(left - 1,right - 1);
cout<<str1[left - 1]; //字符串实际的最后一个字符下标是字符串长度减一
} else if(mem[left][right] == 1){
printLCS(left - 1,right);
} else {
printLCS(left,right - 1);
}
}
int main() {
for(int i=1; i<=str1.length(); i++) {
for(int j=1; j<=str2.length(); j++) {
if(str1[i] == str2[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
mem[i][j] = 0;//左上角转移而来
}
else if(dp[i-1][j]>dp[i][j-1]) {
dp[i][j] = dp[i-1][j];
mem[i][j] = 1;//左边转移
}
else {
dp[i][j] = dp[i][j-1];
mem[i][j] = -1;//上边转移
}
}
}
cout<<dp[str1.length()][str2.length()]<<endl;
printLCS(str1.length(),str2.length());
return 0;
}