1. 程式人生 > >最長公共子序列(LCS問題)的DP解法

最長公共子序列(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;
}