1. 程式人生 > >POJ #1458 Common Subsequence

POJ #1458 Common Subsequence

str get 問題 des 技術分享 sin 個數 bcf std

Description


  問題的描述以及樣例在這裏:1458 Common Subsequence

Sample


  

INPUT: 
    abcfbc               abfcab
    programming          contest 
    abcd                   mnp
OUTPUT:
    4
    2
    0

思路


  首先,我們可以想到用暴力法解決,一個序列的子集有 2n 個,兩個子集相互比較找出相同且元素最多的子集即可。但是算法的運行時間是指數階,肯定會TLE 的。

  可以換個角度思考,從兩個序列的末尾開始比較,總結出求LCS的遞歸式。設序列 X、Y 分別有它們的前綴串,Xm = <x1

, x2, .., xm> 、Yn = <y1, y2, .., yn> ,而序列 Z = <z1, z2, .., zk> 是 X、Y 的任意 LCS。有:

    1.如果 xm = yn ,則 zk = xm = ym ,且 zk-1 是xm-1、yn-1 的一個 LCS

    2.如果 xm != yn,那麽 zk != xm 意味著 zk 是 xm-1 、yn 的一個CLS

    3.如果 xm != yn,那麽 zk != yn 意味著 zk 是 xm 、yn-1 的一個CLS

  舉個例子, <a, b, c> 與 <a, b>,比較兩序列末尾元素 c、b,發現不匹配,那麽就認為它們的 LCS是 <a, b> 與 <a, b> 的LCS,或者是 <a, b, c> 與 <a> 的 LCS,至於是哪個取決於後兩者 LCS 哪個大。如果末尾元素匹配了,說明找到了 LCS中的一個元素,則讓LCS+1 ,繼續尋找 <a, b> 、<a> 的LCS...

  如果我們用 c[i][j] 代替zk 表示LCS的長度,i 表示序列X的長度,j 表示序列Y的長度,有如下遞歸式:

    技術分享圖片

  寫出遞歸的程序,但是發現其實很多子問題是重復求解的,比如 i = 6, j = 5,C[5, 5] 就被重復求解了。像這種的樹型遞歸,我們可以采用記憶化搜索的策略解決,通俗的將就是再寫一個備忘錄,把求過的解都記錄在裏面,下一次問題重復時直接取出其中的解即可。

  這種算法的時間復雜度和互相獨立的子問題個數有關,假設輸入的規模是 m、n,那麽相互獨立的子問題有多少個呢?

  m·n 個,可以這麽想: c[m][n] 是一個,c[m-1][n] 又是一個,c[m][n-1] 又是一個...

  所以,記憶化搜索算法的時間復雜度是O(mn)

#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXSIZE = 1000;
int c[MAXSIZE][MAXSIZE];

int getLCS (const string& x, const string& y,int i, int j) { //傳入的是長度
    if (i == 0 || j == 0) { //長度為 0 時,表示序列為空,此時LCS = 0
        return 0;
    }
    if (c[i][j] >= 0) {
        return c[i][j];
    }
    
    if (x[i-1] == y[j-1]) { //用於比較的是下標,下標= 長度 - 1
        return c[i][j] = getLCS(x, y, i-1, j-1) + 1; 
    }
    else {
        return c[i][j] = std::max(getLCS(x, y, i-1, j), getLCS(x, y, i, j-1));
    }
}

int main(void) {
    string x,y;
    while (cin >> x >> y ) {
        memset (c, -1, sizeof(c)); //LCS可能是0,所以應初始化為-1
        int ans = getLCS(x, y, x.size(), y.size());
        cout << ans << endl;
    }
    return 0;
}

  其實上面的算法就是 DP 的一種,因為它滿足 DP 的三個特點:

    1.將原問題分解成幾個子問題

    2.所有問題只需要解決一次

    3.存儲子問題的解

  但是這種自頂向下的辦法,存儲的空間不密集,會浪費很多空間。

POJ #1458 Common Subsequence