最长公共子序列

        最长公共子序列也称作最长公共子串,英文缩写是LCSLongest Common Subsequence)。其定义是:一个序列S,如果分别是两个或多个已知序列的子序列,且是符合此条件的子序列中最长的,则称S为已知序列的最长公共子序列。

       关于子序列的定义通常有两种方式,一种是对子序列没有连续的要求,其子序列的定义就是原序列中删除若干元素后得到的序列。另一种是对子序列有连续的要求,其子序列的定义是原序列中连续出现的若干个元素组成的序列。求解子序列是非连续的最长公共子序列问题是一个十分实用的问题,它可以描述两段文字之间的相似度,即它们的雷同程度,从而能够用来辨别抄袭

1.1 最优子结构定义与边界值

       现在来分析一下本问题的最优子结构。首先定义问题,假设有字符串str1长度为m,字符串str2长度为n,可以把子问题描述为:求字符串str1<1..m>中从第1个到第ii <= m)个字符组成的子串str1<1…i>和字符串str2<1..n>中从第1个到第j(j <= n)个字符组成的子串str2<1…j>的最长公共序列,子问题的最长公共序列可以描述为d[i,j] = { Z1,Z2, … Zk },其中Z1-Zk为当前子问题已经匹配到的最长公共子序列的字符。子问题定义以后,还要找出子问题的最优序列d[i,j]的递推关系。分析d[i,j]的递推关系要从str1[i]str2[j]的关系入手,如果str1[i]str2[j]相同,则d[i,j]就是d[i-1,j-1]的最长公共序列+1Zk=str1[i]=str2[j]如果str1[i]str2[j]不相同,则d[i,j]就是d[i-1,j]的最长公共序列和d[i,j-1]的最长公共序列中较大的那一个。

最后是确定d[i,j]的边界值,当字符串str1为空或字符串str2为空时,其最长公共子串应该是0,也就是说当i=0j=0d[i,j]就是0d[i,j]的完整递推关系如下:

d[i,j]=0; i=j=0

d[i.j] = d[i-1,j-1];str[i]==str[j]

d[i,j] = max(d[i][j-1],d[i-1][j]);str[i]!=str[j]

反求最长公共子序列

       根据1.1得到的最优解子结构递推关系,依次计算i从到mj1nd[i,j]值,最后得到的d[m,n]就是最长公共子序列的长度。d[m,n]只是最长公共子序列的长度值,表示了两个字符串的相似程度,如果要获得最长公共子序列,就需要在计算出d[m,n]矩阵的值后分析每一步决策的结果根据每一个最优决策逆向构造出最长公共子序列。为此需要在递推计算d[i,j]的过程中,需要同时记录下最优决策的过程,最优决策的过程用矩阵r表示,r[i,j]表示最长公共子序列的长度值d[i,j]递推来源。根据前面整理的递推关系,如果r[i,j]的值是1,则表示d[i,j]的值由d[i-1,j-1] + 1递推得到;如果r[i,j]的值是2,则表示d[i,j]的值由d[i-1,j]递推得到;如果r[i,j]的值是3,则表示d[i,j]的值由d[i,j-1]递推得到。以字符串“abcdea”“aebcda”为例,根据递推关系得到的dr合并到一个矩阵中显示

 

逆向构造最长公共子串的过程从r[m,n]开始,如果r[i,j]1,则表示两个字符串中的str1[i]str2[j]相同,可以将str1[i]str2[j]插入到当前构造的最长公共子序列中。如果r[i,j]≠1,则不改变当前构造的最长公共子序列,但是要根据r[i,j]的值是2还是3,调整r[i,j]倒推的下一个位置。以上述两个字符串构造出的d矩阵和r矩阵为例,逆向构造最长公共子序列的过程如下:r[6,6]=1表示当前公共子串lcs = <str1[6]>str1[6]<str2[6]>值一样),同时前一次最优决策来自于d[5,5]r[5,5]=2表示前一次决策来自于d[4,5],此时r[4,5]=1,表示当前公共子串lcs = < str1[4]str1[6]>,同时前一次最优决策来自于d[3,4]r[3,4]=1表示当前公共子串lcs = < str1[3]str1[4]str1[6]>,同时前一次最优决策来自于d[2,3]r[2,3]=1表示当前公共子串lcs = < str1[2]str1[3]str1[4]str1[6]>,同时前一次最优决策来自于d[1,2]r[1,2]=3表示前一次决策来自于d[1,1],此时r[1,1]=1,表示当前公共子串lcs = < str1[1]str1[2]str1[3]str1[4]str1[6]>。由于r[0,0]是边界,因此逆向构造过程结束,得到最长公共子串的最终结果是lcs = < str1[1]str1[2]str1[3]str1[4]str1[6]>,对应的字符串就是<abcda>

 

#include <string.h>
#include <curses.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <time.h>
 
#define MAX_STRING_LEN 64
typedef struct tagDPLS
{
 int d; /*鏈€浼樺喅绛栧€*/
 int r; /*鍐崇瓥鏂瑰悜*/
}DPLS;


int InitializeDplcs(char *str1, char *str2, DPLS dp[MAX_STRING_LEN][MAX_STRING_LEN])
{
 int i,j;
 
 for(i = 1; i<= strlen(str1);i++){
  dp[i][0].d = 0;
 }
 for(j = 1; j<= strlen(str2);j++){
  dp[0][j].d = 0;
 }
 
 for(i = 1; i<=strlen(str1);i++){
  for(j = 1; j<= strlen(str2);j++){
   if(str1[i -1] == str2[j-1]){
    dp[i][j].d = dp[i-1][j-1].d + 1;
    dp[i][j].r = 1;
   }
   else{
    if(dp[i-1][j].d >= dp[i][j-1].d){
     dp[i][j].d = dp[i-1][j].d;
     dp[i][j].r = 2;
    }else{
     dp[i][j].d = dp[i][j-1].d;
     dp[i][j].r =3;
    }
   }
  }
 }
 return dp[strlen(str1)][strlen(str2)].d;
}
void GetLcs(DPLS dp[MAX_STRING_LEN][MAX_STRING_LEN],int i,int j, char *str1,char *lcs)
{
 if(i ==0 || j==0){
  return;
 }
 if(dp[i][j].r == 1){
  GetLcs(dp,i-1,j-1,str1,lcs);
  sprintf(lcs, "%s%c",lcs,str1[i-1]);
 }else{
  if(dp[i][j].r == 2)
   GetLcs(dp,i -1,j,str1,lcs);
  else
   GetLcs(dp,i,j-1,str1,lcs);
 }
}

#include <string.h>
enum decreaseDir {
 kInt = 0,
 kLeft,
 kUp,
 kLeftUp,
};

 

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){
  return;
 } 
 if(LCS_direction[row][col] == kLeftUp){
  if(row > 0 && col>0){
   LCS_Print(LCS_direction,pStr1,pStr2,row-1,col -1);
   printf("%c",pStr1[row]);
  }
 }else if(LCS_direction[row][col] == kLeft){
  if( col>0){
   LCS_Print(LCS_direction,pStr1,pStr2,row,col -1);
  }
 }else if(LCS_direction[row][col] == kUp){
  if( row>0){
   LCS_Print(LCS_direction,pStr1,pStr2,row-1,col);
  }
 }
 
 
}

int LCS(char *pStr1, char *pStr2)
{
 if(!pStr1 || !pStr2){
  return 1;
 }
 size_t length1 = strlen(pStr1);
 size_t length2 = strlen(pStr2);
 if(!length1 || !length2){
  return 0;
 }
 size_t i,j;
 int **LCS_length;
 LCS_length = (int **)malloc(length1*sizeof(int));
 for(i =0; i<length1;i++ ){
  LCS_length[i] = (int *)malloc(length2*sizeof(int));
 }
 for(i=0; i<length1; ++i){
  for(j=0; j<length2; ++j){
   LCS_length[i][j] = 0;
  }
 }
 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] = kInt;
  }
 }
 
 for(i=0; i<length1; ++i){
  for(j=0; j<length2; j++){
   if(i == 0|| j==0){
    if(pStr1 == pStr2){
     LCS_length[i][j] = 1;
     LCS_direction[i][j] = kLeftUp;
    }else{
     LCS_length[i][j] = 0;
    }
   }else if(pStr1[i] == pStr2[j]){
    LCS_length[i][j] = LCS_length[i-1][j-1];
    LCS_direction[i][j] = kLeftUp;
   }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;
   }else if(LCS_length[i-1][j] < LCS_length[i][j-1]){
    LCS_length[i][j] = LCS_length[i][j-1];
    LCS_direction[i][j] = kLeft;
   }
  }
 }
 LCS_Print(LCS_direction,pStr1,pStr2,length1-1,length2-1);
 return LCS_length[length1 -1][length2-1];
}
int GetLongestString(char *strTmp1, char *strTmp2, char *strTmp3)
{
 int i = strlen(strTmp1);
 int j = strlen(strTmp2);
 int k = strlen(strTmp3);
 
 return i>j?i:((j>k)?j:k);
 
 
}

void RecursionLCS( char *str1,  char *str2, char *lcs)
{
 if(strlen(str1)== 0 || strlen(str2) == 0)
  return;
 
 if(str1[0]== str2[0])
 {
  lcs += str1[0];
  RecursionLCS(++str1, ++str2, lcs);
 }
 else
 {
  char *strTmp1,*strTmp2,*strTmp3;
 
  RecursionLCS(str1++, str2, strTmp1); //尝试删除str1
  RecursionLCS(str1, str2++, strTmp2); //尝试删除str2
  RecursionLCS(str1++, str2++, strTmp3); //尝试同时删除str1和str2
  lcs += GetLongestString(strTmp1, strTmp2, strTmp3);
 }

}

int main()
{
 char str1[] = "cdea";
 char str2[] = "bcda";
 char lcs[20],lcs1[20];
 DPLS dp[MAX_STRING_LEN][MAX_STRING_LEN];
 int i, j ;
 
 InitializeDplcs(str1,str2,dp) ;//静态的分配的动态规划实现
 
 GetLcs(dp,strlen(str1),strlen(str2), str1,lcs);
 printf("%s\n",lcs);
 printf("=============\n");
 LCS(str1,str2);//动态分配的动态规划法实现
 
 //RecursionLCS(str1, str2, lcs1);//调试未通过,穷举法
 printf("%s\n",lcs1); 
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值