动态规划:求最长公共子序列问题

一、最长公共子序列问题(注意:不是连续子序列): 

子序列是指在原序列中删去若干元素(这些元素可以不相邻)后得到的序列。例如X=abcbdab,Y=bdcaba,bca和bcba都是X和Y的公共子序列,且后者是最长的公共子序列。给定两个序列X(i)={x[1],x[2],......x[i]}和Y(j)={y[1],y[2],......y[j]},如何找出它们的一个最长公共子序列?

动态规划算法求解过程如下:

1. 设c[i][j]表示X(i)和Y(j)的最长公共子序列长度。

(1)若x[i]=y[j],则X(i)和Y(j)的最长公共子序列c[i][j]=c[i-1][j-1]+1。

(2)若x[i]!=y[j],必须解决两个子问题,即找出X(i-1)和Y(j)的一个最长公共子序列及X(i)和Y(j-1)的一个最长公共子序列,这两者中的较长者即为原问题的解,因此c[i][j]=max{c[i][j-1],c[i-1][j]}。

(3)为打印出最长公共子序列,用b[i][j]标识c[i][j]是由上述三种子问题中的哪一种计算出来的,是由X(i-1)和Y(j-1)的LCS在尾部加上x[i]所得到,还是由X(i-1)和Y(j)的LCS得到,还是由X(i)和Y(j-1)的LCS得到。根据b[i][j]我们可以递归地打印出最长公共子序列的结果。

2. 具体计算c[i][j]如下:

void LCSLength(int m,int n,char* x,char* y,int** c,char** b){  
    int i,j;  
    for(i=1;i<=m;i++) c[i][0]=0;  
    for(i=1;i<=n;i++) c[0][i]=0;  
      
    for(i=1;i<=m;i++)  
        for(j=1;j<=n;j++)  
            if(x[i]==y[j]){  
                c[i][j]=c[i-1][j-1]+1;  
                b[i][j]='A';  
            }else if(c[i-1][j]>=c[i][j-1]){  
                c[i][j]=c[i-1][j];  
                b[i][j]='B';  
            }else{  
                c[i][j]=c[i][j-1];  
                b[i][j]='C';  
            }  
}  

3. 根据计算最优值得到的信息构造最优解:b[i][j]保存了计算c[i][j]的方式,据此可构造出最优解。

void LCS(int i,int j,char* x,char** b){  
    if(i==0 || j==0) return;  
    if(b[i][j]=='A'){  
        LCS(i-1,j-1,x,b);  
        cout<<x[i];  
    }else if(b[i][j]=='B'){  
        LCS(i-1,j,x,b);  
    }else {  
        LCS(i,j-1,x,b);  
    }  
}  

二、如果将上述问题更改为:求最长公共子串(注意:是连续的),该如何解答呢?

仍然用动态规划来解决,但状态方程的定义需要改一下:

1. 如果序列X和序列Y存在最长公共子串,那么这个子串肯定是X、Y序列的一部分,必然是以某个x[i], y[j]结尾(且x[i] == y[j])。那么我们就可以这样来定义:假设X、Y存在以x[i],y[j] 结尾的公共子串,用c[i][j] 表示这个公共子串可能的最大长度。很明显有如下状态方程:

(1)若x[i]=y[j],则c[i][j]=c[i-1][j-1]+1

(2)若x[i]!=y[j],则c[i][j]=0

是不是很简单,跟上面的计算方式几乎一摸一样,只需要在x[i]==y[j]的时候,直接给c[i][j] = 0 就可以了。

void LCSLength(int m,int n,char* x,char* y,int** c){  
    int i,j;  
    for(i=1;i<=m;i++) c[i][0]=0;  
    for(i=1;i<=n;i++) c[0][i]=0;  
      
    for(i=1;i<=m;i++) {
        for(j=1;j<=n;j++) { 
            if (x[i] == y[j]) {  
                c[i][j] = c[i-1][j-1]+1;  
            } else {  
                c[i][j] = 0;  
            }  
        } 
    } 
}  
 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值