最长公共子序列(LCS问题)的DP解法

呃。。大一做过,毕竟是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;
}



  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值