呃。。大一做过,毕竟是ACM入门DP题,但是大三的我已然忘了具体咋做了,只记得是DP,面试常会问这个问题,所以有必要搞明白。
题目描述略。
解题思想就是DP,DP无外乎需要知道两个东西,一是状态是什么,二是状态之间的递推关系是什么。
这道题是一个二维DP,使用状态dp[i][j]表示str1取到第i个字符(包括i),str2取到第j个字符(包括j)时,最长公共子序列的长度。(i,j取值从1开始)
递推关系为:
dp[i][j] = 0 (i = 0或j = 0)
dp[i][j] = max(
dp[i-1][j-1] + 1, (若str1[i] = str2[j])
dp[i-1][j],
dp[i][j-1]
);
综上,就可以写出一个求最长公共子序列长度的代码了,可以用HDU1159验证,该题是LCS模板题。
//注意下面代码里实际存储的字符串是从0开始,dp数组是从1开始。
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int dp[1000][1000];
int main(){
string str1,str2;
while(cin>>str1>>str2){
memset(dp, 0, sizeof(dp));
int m = str1.length();
int n = str2.length();
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1[i-1] == str2[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
}
dp[i][j] = max(dp[i-1][j], dp[i][j]);
dp[i][j] = max(dp[i][j-1], dp[i][j]);
}
}
cout<<dp[m][n]<<endl;
}
return 0;
}
然后笔试面试时候,往往还会让你输出这个最长公共子序列,我使用的方法是建立一个结构体,每个DP状态上都保留了该状态是从哪个状态递推来的,以及该状态上是否增添了新的相等字符。这样就可以从dp[m][n]状态一直回溯到该状态最开始是从什么状态推出来的了,也就知道了最长公共子序列是什么。
代码如下,该代码里注释部分可以调试输出,方便理解:
#include <cstdio>
#include <iostream>
#include <string>
#include <stack>
#include <cstring>
using namespace std;
struct NODE{
int len;
int preX;
int preY;
bool selected;
NODE(){
len = 0;
preX = -1;
preY = -1;
selected = false;
}
NODE(int l, int x, int y, bool s){
len = l;
preX = x;
preY = y;
selected = s;
}
};
NODE dp[1000][1000];
string str1,str2;
int m,n;
void printLCS(){
cout<<"最长公共子序列长度为:"<<dp[m][n].len<<endl;
cout<<"该序列为:";
stack<char> st;
int i = m, j = n;
while(i!=-1 && j!=-1){
//cout<<i<<" "<<j<<" get "<<dp[i][j].preX<<" "<<dp[i][j].preY<<endl;
if(dp[i][j].selected){
st.push(str1[i-1]);
}
int preI = dp[i][j].preX;
int preJ = dp[i][j].preY;
i = preI, j = preJ;
}
if(st.empty()){
cout<<"无"<<endl;
}
while(!st.empty()){
cout<<st.top();
st.pop();
}
cout<<endl<<"---------------------------------"<<endl;
}
int main(){
while(cin>>str1>>str2){
m = str1.length();
n = str2.length();
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
dp[i][j] = NODE();
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str1[i-1] == str2[j-1]){
dp[i][j] = NODE(dp[i-1][j-1].len + 1, i-1, j-1, true);
//cout<<i<<" "<<j<<" is from "<<i-1<<" "<<j-1<<" val is "<<dp[i][j].len<<" s is true"<<endl;
}
if(dp[i-1][j].len>dp[i][j].len){
dp[i][j] = NODE(dp[i-1][j].len, i-1, j, false);
//cout<<i<<" "<<j<<" is from "<<i-1<<" "<<j<<" val is "<<dp[i][j].len<<" s is false"<<endl;
}
if(dp[i][j-1].len>dp[i][j].len){
dp[i][j] = NODE(dp[i][j-1].len, i, j-1, false);
//cout<<i<<" "<<j<<" is from "<<i<<" "<<j-1<<" val is "<<dp[i][j].len<<" s is false"<<endl;
}
}
}
/*for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
cout<<dp[i][j].len<<" ";
}
cout<<endl;
}*/
printLCS();
}
return 0;
}