动态规划 -- 最长公共子序列

动态规划之最长公共子序列

参考资料

1 算法导论
何海涛

最长公共子序列(Longest Common Subsequence)

给定两个序列 X 和 Y ,称序列 Z 是 X 和 Y 的公共子序列,如果 Z 即是 X 的一个子序列又是 Y 的一个子序列, 该公共子序列不要求是连续的,只是要求Z的元素在X和Y中是以相同的顺序出现。例如,如果 X = { A , B , C , B , D , A , B } ,Y = { B , D , C , A , B , A } ,则序列 { B , C , B , A } 即为 X 和 Y 的一个公共子序列。序列 { B , C, A } 不是 X 和 Y 的一个最长公共子序列,因为长度只是3 ,不是最长的。

递归解

在找X= ( x1,⋯,xm )和 Y = ( y1,⋯,yn ) 的一个 LCS时,需要检查一个或两个子问题。如果 xm = yn,必须找出 xm-1 和 yn-1的一个LCS。将 xm = yn 添加到这个 LCS上,可以产生 X 和 Y 的一个 LCS 。如果 xm != yn ,就必须解决两个子问题,找出xm-1和Y 的一个LCS,以及找出 X 和 yn-1 的一个LCS 。这两个LCS中,较长的就是 X和 Y 的一个 LCS 。

所以 LCS的递归式如下:


计算 LCS 的长度


LCS-LENGTH ( X , Y)
     m  ← length[ X ]
     n   ← length[ Y ]
    for  i ← 1 to m
	do c[i,0] ← 0
    for  j ← 0 to n 
	do  c[0,j] ← 0
    for i ← 1 to m
	do  for  j ← 1 to n
		do if  xi = yj
			then c[i,j]  ← c[i-1,j-1] +1
				 b[i,j] ← “ \”
			else if c[i-1,j] > c[i,j-1]
			    then  c[i,j] ← c[i-1,j]
				      b[i,j] ← " |"
			else c[i,j] ← c[i,j-1]
				b[i,j] ← “---”
   return c and b

构造一个LCS 

  1.    
PRINT-LCS(b,X,i,j) 
  if i=0 or j=0 
	then return;  
  if b[i,j]="\" 
	then LCS(b,X,i-1,j-1);  
            print(x[i]);  
  else if b[i,j]="|" 
           then LCS(b,X,i-1,j)   
  else LCS(b,X,i,j-1); 

代码实现

#include "string.h"

// directions of LCS generation
enum decreaseDir {kInit = 0, kLeft, kUp, kLeftUp};

/
// Get the length of two strings' LCSs, and print one of the LCSs
// Input: pStr1         - the first string
//        pStr2         - the second string
// Output: the length of two strings' LCSs
/
int LCS(char* pStr1, char* pStr2)
{
      if(!pStr1 || !pStr2)
            return 0;

      size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);
      if(!length1 || !length2)
            return 0;

      size_t i, j;

      // initiate the length matrix
      int **LCS_length;
      LCS_length = (int**)(new int[length1]);
      for(i = 0; i < length1; ++ i)
            LCS_length[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_length[i][j] = 0;

      // initiate the direction matrix
      int **LCS_direction;
      LCS_direction = (int**)(new int[length1]);
      for( i = 0; i < length1; ++ i)
            LCS_direction[i] = (int*)new int[length2];

      for(i = 0; i < length1; ++ i)
            for(j = 0; j < length2; ++ j)
                  LCS_direction[i][j] = kInit;

      for(i = 0; i < length1; ++ i)
      {
            for(j = 0; j < length2; ++ j)
            {
                  if(i == 0 || j == 0)
                  {
                        if(pStr1[i] == pStr2[j])
                        {
                              LCS_length[i][j] = 1;
                              LCS_direction[i][j] = kLeftUp;
                        }
                        else
                              LCS_length[i][j] = 0;
                  }
                  // a char of LCS is found, 
                  // it comes from the left up entry in the direction matrix
                  else if(pStr1[i] == pStr2[j])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j - 1] + 1;
                        LCS_direction[i][j] = kLeftUp;
                  }
                  // it comes from the up entry in the direction matrix
                  else if(LCS_length[i - 1][j] > LCS_length[i][j - 1])
                  {
                        LCS_length[i][j] = LCS_length[i - 1][j];
                        LCS_direction[i][j] = kUp;
                  }
                  // it comes from the left entry in the direction matrix
                  else
                  {
                        LCS_length[i][j] = LCS_length[i][j - 1];
                        LCS_direction[i][j] = kLeft;
                  }
            }
      }
      LCS_Print(LCS_direction, pStr1, pStr2, length1 - 1, length2 - 1);
     for(i = 0; i < length1; i++) 
     { 
        delete [] LCS_length[i];
        delete [] LCS_direction[i];
     }  
      
     return LCS_length[length1 - 1][length2 - 1];
}


/
// Print a LCS for two strings
// Input: LCS_direction - a 2d matrix which records the direction of 
//                        LCS generation
//        pStr1         - the first string
//        pStr2         - the second string
//        row           - the row index in the matrix LCS_direction
//        col           - the column index in the matrix LCS_direction
/
void LCS_Print(int **LCS_direction, 
                    char* pStr1, char* pStr2, 
                    size_t row, size_t col)
{
      if(pStr1 == NULL || pStr2 == NULL)
            return;

      size_t length1 = strlen(pStr1);
      size_t length2 = strlen(pStr2);

      if(length1 == 0 || length2 == 0 || !(row < length1 && col < length2))
            return;

      // kLeftUp implies a char in the LCS is found
      if(LCS_direction[row][col] == kLeftUp)
      {
            if(row > 0 && col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col - 1);

            // print the char
            printf("%c", pStr1[row]);
      }
      else if(LCS_direction[row][col] == kLeft)
      {
            // move to the left entry in the direction matrix
            if(col > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row, col - 1);
      }
      else if(LCS_direction[row][col] == kUp)
      {
            // move to the up entry in the direction matrix
            if(row > 0)
                  LCS_Print(LCS_direction, pStr1, pStr2, row - 1, col);
      }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最长公共子序列问题(Longest Common Subsequence,简称LCS)是指在两个序列中找到一个最长的公共子序列,其中一个序列的所有元素按原序列中出现的顺序排列,而另一个序列中的元素则不要求按原序列中出现的顺序排列。 动态规划方法可以很好地解决LCS问题。设A和B是两个序列,LCS(A,B)表示A和B的最长公共子序列。则可以设计如下的状态转移方程: 当A和B的末尾元素相同时,LCS(A,B) = LCS(A-1,B-1) + 1。 当A和B的末尾元素不同时,LCS(A,B) = max(LCS(A-1,B), LCS(A,B-1))。 其中,LCS(A-1,B-1)表示A和B的末尾元素相同时的情况,LCS(A-1,B)表示A的最后一个元素不在最长公共子序列中,而B中的最后一个元素在最长公共子序列中的情况,LCS(A,B-1)表示B的最后一个元素不在最长公共子序列中,而A中的最后一个元素在最长公共子序列中的情况。 根据这个状态转移方程,可以使用动态规划算法来求解LCS问题。具体方法是,构建一个二维数组dp,其中dp[i][j]表示A前i个元素和B前j个元素的LCS。初始化dp[0][j]和dp[i][0]为0,然后按照上述状态转移方程进行递推,最终得到dp[lenA][lenB],其中lenA和lenB分别表示A和B的长度。dp[lenA][lenB]即为A和B的最长公共子序列的长度。要找到具体的最长公共子序列,可以从dp[lenA][lenB]开始,按照状态转移方程反向推导出每个元素,即可得到最长公共子序列。 LCS问题是动态规划算法的经典应用之一,时间复杂度为O(n*m),其中n和m分别为A和B的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值