DP專題 3 | LCS最長公共子序列 - POJ 1458

Common Subsequence(公共子序列問題)

 

Description

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, xij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

 

簡單說就是Z是X的子序列。給定序列X和Y,求出最長公共子序列的長度。

 

Input

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

 

每行是兩個字符串序列。

 

Output

For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

 

輸出最長公共子序列的長度。

 

Sample Input

abcfbc         abfcab 
programming    contest 
abcd           mnp

Sample Output

4 
2
0

 

LCS算法是一個二維DP,核心思想是:

 

當A[i]等於A[j]時,dp[i][j] = dp[i-1][j-1]+1,表示增加了一個元素。

如果A[i]和A[j]不相等,則不增加元素,但是要記錄dp值,因爲後續需要依賴該值,dp[i][j] = max(dp[i-1][j], dp[i][j-1])。

 

實現採用記憶化遞歸的方式非常自然簡單,缺點是性能差,雖然LCS可以採用遞推的方式做,而且從dp[0][0]開始遞推也比較容易實現,但是注意dp[i][0]都爲0,dp[0][j]都爲0,採用二維循環,對於數列a和b,每次遍歷b都是固定a的一個值,其實是計算表中的一行。理解起來比遞歸要複雜些。

 

下圖可以幫助理解一下~

 

 

源代碼:記憶化遞歸

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <set>
#include <string>
#include <vector>
using namespace std;

int d[1000][1000];
string s1, s2;

int dp(int i, int j)
{
    if(d[i][j] != -1)
        return d[i][j];

    if(i==0 || j==0)
        return 0;

    if(s1[i-1] == s2[j-1])
        d[i][j] = dp(i-1, j-1) + 1;
    else
        d[i][j] = std::max(dp(i-1, j), dp(i, j-1));

    return d[i][j];
}

int main()
{
    while(cin>>s1>>s2)
    {
        memset(d, -1, sizeof(d));
        cout << dp(s1.length(), s2.length()) <<endl;
    }

    return 0;
}

源代碼:遞推 0ms

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <set>
#include <string>
#include <vector>
using namespace std;

char s1[1000];
char s2[1000];
int dp[1000][1000];

int main() {
    while (cin >> s1 >> s2) {
        int l1 = strlen(s1);
        int l2 = strlen(s2);
        int i, j;

        for (i = 0; i <= l1; i++)
            dp[i][0] = 0;

        for (j = 0; j <= l2; j++)
            dp[0][j] = 0;

        for (i = 1; i <= l1; i++) {
            for (j = 1; j <= l2; j++) {
                if (s1[i - 1] == s2[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
            }
        }
        cout << dp[l1][l2] << endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章