最長公共子序列,最大遞增子序列,最長公共遞增子序列


Common Subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 21529    Accepted Submission(s): 9351


Problem 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. 
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. 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
 
【題意】
就是最長公共子序列。
(看的時候最好畫個表幫助理解)
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dp[1000][1000];//dp[i][j]表示由s1的前i位與s2的前j位所得到的最長公共子序列長度(注意dp從1開始,字符串下標從0開始)
int main()
{
    char s1[1000],s2[1000];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        int i,j,lenth1=strlen(s1),lenth2=strlen(s2);
        for(i=0;i<lenth1;i++)
        {
            for(j=0;j<lenth2;j++)
            {
                if(s1[i]==s2[j])//若s1的第i(從0開始)位與s2的第j位相匹配。
                  dp[i+1][j+1]=dp[i][j]+1;//則s1的前i位與s2的前j位所得到的最長公共子序列長度=s1的前i-1位與s2的前j-1位所得到的最長公共子序列長度+1
                else
                  dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);//不匹配的話就有兩種情況,取最大值就可以了。
            }
        }
        printf("%d\n",dp[lenth1][lenth2]);
    }
    return 0;
}
注意到:
dp[i][j]取值只與dp[i-1][j-1],dp[i-1][j],dp[i][j-1]相關。即只與當前行,上一行有關。那麼就想到是否可以像一般的動態規劃一樣用一維dp[1000](表示字符串s1,與dp[j](s2的前i位組成的字符串的最長公共子序列長度),通過不斷更新dp[1000]來得到最後的結果呢?
假設可以,觀察這兩個表達式   dp[i-1][j-1]  dp[i][j-1]。當我們計算完dp[j-1]後,丟失了dp[i-1][j-1]的數據,當計算dp[j]的時候,我們可能無法得到正確的結果。
得證,無法用一個一維數組dp[N]是程序算出正確結果。又觀察到dp[i][j]的確只與當前行,上一行有關,就想到用兩個數組來分別存儲當前dp[N]、上一行dp[N]。這樣就可以寫出正確的動態轉移方程。
即:
if(s1[i]==s2[j])
    dp[當前行][j+1]=dp[上一行][j]+1;
else
    dp[當前行][j+1]=max(dp[當前行][j],dp[上一行][j+1]);


滾動數組
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dp[2][1000];//滾動數組,dp[0],dp[1]交替表示上一次,當前的dp。
int main()
{
    char s1[1000],s2[1000];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        int i,j,lenth1=strlen(s1),lenth2=strlen(s2);
        int e=0;
        for(i=0;i<lenth1;i++)
        {
            e=1-e;//e是當前行
            for(j=0;j<lenth2;j++)
            {
                if(s1[i]==s2[j])
                    dp[e][j+1]=dp[1-e][j]+1;//1-e是上一行
                else
                    dp[e][j+1]=max(dp[e][j],dp[1-e][j+1]);
            }
        }
        printf("%d\n",dp[e][lenth2]);
    }
    return 0;
}


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