最長公共子序列(LCS)——?jiǎng)討B(tài)規(guī)劃

問題描述

??子序列是指,從序列中選出一些子元素,需滿足其前后關(guān)系與在原序列中相同;公共是指該序列同時(shí)是兩個(gè)序列的子序列。如兩個(gè)序列{4,2,1 ,6,5,8,13,18,9,5}和 {3,2,1,9,10,6,5,9},{2,1}和{1,6,5}都是它們的公共子序列,顯然,這不是最長的。那么如何求解兩個(gè)序列的最長公共子序列(LCS)?

分析

??LCS具有最優(yōu)子結(jié)構(gòu)。令X=<x_{1}, x_{2}, ..., x_{m}>Y=<y_{1}, y_{2}, ..., y_{n}>為兩個(gè)序列,Z=<z_{1}, z_{2}, ..., z_{k}>為X和Y的任意LCS。

  1. 如果x_{m}=y_{n},則z_{k}=x_{m}=y_{n}Z_{k-1}X_{m-1}Y_{n-1}的一個(gè)LCS;
  2. 如果x_{m}≠y_{n},那么z_{k}≠x_{m},意味著ZX_{m-1}Y的一個(gè)LCS;
  3. 如果x_{m}≠y_{n},那么z_{k}≠y_{n},意味著ZXY_{n-1}的一個(gè)LCS.

??從上面分析可以看出,當(dāng)x_{m}=y_{n}時(shí),需要計(jì)算X_{m-1}Y_{n-1}的一個(gè)LCS;當(dāng)x_{m}≠y_{n}時(shí),需要分別計(jì)算X_{m-1}Y的一個(gè)LCS、XY_{n-1}的一個(gè)LCS。具有很明顯的狀態(tài)轉(zhuǎn)移含義,這些重疊的子問題可以用動(dòng)態(tài)規(guī)劃方法快速解決。
??根據(jù)LCS問題的最優(yōu)子結(jié)構(gòu)性質(zhì),有如下遞歸公式:

狀態(tài)轉(zhuǎn)移方程

??二維數(shù)組dp[i][j]表示序列<x_{1}, x_{2}, ..., x_{i}>與序列<y_{1}, y_{2}, ..., y_{j}>的最長公共子序列長度。根據(jù)題目中的數(shù)據(jù),可以畫出如下序列增長過程,看出此動(dòng)態(tài)規(guī)劃算法的時(shí)間復(fù)雜度為O(nm),因?yàn)槊總€(gè)表項(xiàng)的計(jì)算時(shí)間是O(1).

序列增長過程

代碼實(shí)現(xiàn)

#include <iostream>
#include <cstring>

using namespace std;
const int maxn = 205;
int dp[maxn][maxn];
int arr1[maxn], arr2[maxn];

int main()
{
    int n, m;
    memset(dp,0,sizeof(dp));
    memset(arr1,0,sizeof(arr1));
    memset(arr2,0,sizeof(arr2));
    cin >> n; for(int i=1;i<=n;i++) cin>>arr1[i];
    cin >> m; for(int i=1;i<=m;i++) cin>>arr2[i];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(arr1[i]==arr2[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[n][m];
    return 0;
}

Tips

  • 最長公共子序列和編輯距離,都是衡量字符串相似度的指標(biāo),也都是動(dòng)態(tài)規(guī)劃算法的典型。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容