最长公共子序列O(n*m)

转载:
作者 :JarvisChu
出处:http://blog.csdn.net/jarvischu

最长公共子序列
问题
若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:zj=xij。例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。

给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。

给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

分析
序列X和Y使用数组表示:X[1…N],Y[1…N],使用二维数组Z[N+1][M+1]定义解,其中Z[i][j] 表示 {X1,…,Xi}与{Y1,…,Yj}的最长公共子序列长度。如:Z[1][2]表示 {X1} 与 {Y1,Y2}

由上面的定义可知:

Z[i][0] 表示X{…} 与 空序列Y {} 的公共子序列,故Z[i][0] = 0

Z[0][j] 表示空序列X {}{} 与Y {…} 的公共子序列,故Z[0][j]= 0

递推公式如下:
这里写图片描述
为了求得最长公共子序列的具体值,需要在求解公共子序列长度的时候,添加标志(或称为备忘)以记录求解过程的每一步,便于在得到长度后,回溯求解过程,得到具体的序列。

下面的代码中使用how[N+1][M+1] 完成这一功能。

代码:

[cpp] view plain copy
/************************************************************************  
 * 名  称:LCS.cpp 
 * 功  能:动态规划算法案例:最长公共子序列  
 * 作  者:JarvisChu  
 * 时  间:2013-11-8  
 ************************************************************************/   

#include <iostream>  

#define N 8  
#define M 7  

char X[N+1]={' ','A','B','C','D','E','F','G','H'};// 序列 X   X0不用  
char Y[M+1]={' ','B','D','G','H','A','F','A'};    // 序列 Y Y0不用  

//#define N 4  
//#define M 3  
//char X[N+1]={' ','A','B','C','D'};// 序列 X X0不用  
//char Y[M+1]={' ','A','B','D'};    // 序列 Y Y0不用  


int Z[N+1][M+1]; //Z[i][j] 表示 {X1,...,Xi}与{Y1,...,Yj}的最长公共子序列长度  
                 //Z[0][0] 表示 {} 与 {}  
                 //Z[1][2] 表示 {X1} 与 {Y1,Y2}  

int how[N+1][M+1]; //记录最长公子序列是如何得到的,用来追溯Z,得到具体的公共子序列  
                   //how[i][j] = 0 表明记录 Z[i][j] = Z[i-1][j-1]+1,  X[i] == Y[j] 时;  
                   //how[i][j] = 1 表明记录 Z[i][j] = Z[i-1][j],  X[i] != Y[j] 时;  
                   //how[i][j] = 2 表明记录 Z[i][j] = Z[i][j-1],  X[i] != Y[j] 时;  

/*----------------------------------------------------------------------------------  
 * 功  能: 求序列 X和Y的最长公共子序列的长度 [求最优解的值] 
 * 参  数: 
 * 返  回:无  
 ------------------------------------------------------------------------------------*/    
void LCSLength()  
{  
    int i,j;  

    //X的{} 和 Y的{}{Y1}...{Y1,Y2}的公共子序列长度为0;同理有Y的{}  
    for(i=0;i<N;i++) Z[i][0] = 0;  
    for(j=0;j<M;j++) Z[0][j] = 0;  

    //考虑其他情况,X和Y都顺序增长  
    for(i=1;i<=N;i++)  
    {  
        for(j=1;j<=M;j++)  
        {  
            //X,Y当前元素相等,公共子序列长度加1  
            if(X[i] == Y[j])  
            {  
                Z[i][j] = Z[i-1][j-1]+1;  
                how[i][j] = 0;  
            }  

             //Z[i][j] = max(Z[i-1][j],Z[i][j-1]);  

            else if(Z[i-1][j] >= Z[i][j-1])  
            {  
               Z[i][j] = Z[i-1][j];  
               how[i][j] = 1;  
            }  
            else  
            {  
                Z[i][j] = Z[i][j-1];  
                how[i][j] = 2;  
            }  
        }  
    }  
}  


/*----------------------------------------------------------------------------------  
 * 功  能: 追溯Z,得到具体的公共子序列 [求最优解]  
 * 参  数: 
 * 返  回:无  
 ------------------------------------------------------------------------------------*/    
void LCS(int i,int j)  
{  
    if(i==0 || j==0) return ;  

    if(how[i][j] == 0)   
    {  
        LCS(i-1,j-1);  
        std::cout<<X[i]<<" ";  
    }  
    else if(how[i][j] == 1)  
    {  
        LCS(i-1,j);  
    }  
    else  
    {  
        LCS(i,j-1);  
    }  
}  

int main()  
{  
    //最长公共子序列的长度  
    LCSLength();  
    std::cout<<Z[N][M]<<std::endl;//长度  

    //for(int i=0;i<=N;i++)  
    //{  
    //    for(int j=0;j<=M;j++)  
    //    {  
    //        std::cout<<Z[i][j]<<"  ";  
    //    }  
    //    std::cout<<std::endl;  
    //}  

    LCS(N,M);  
    return 0;  
}  

猜你喜欢

转载自blog.csdn.net/HevenYin/article/details/79565378
今日推荐