最长公共子序列问题
  ljGBcG8XAdp8 2023年11月01日 64 0

最长公共子序列和最长公共子串区别

最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别:

子串要求在原字符串中是连续的,而子序列则只需保持相对顺序一致,并不要求连续。

例如  X = {a, Q, 1, 1}; Y = {a, 1, 1, d, f}

那么,{a, 1, 1}是X和Y的最长公共子序列,但不是它们的最长公共字串。

最优子结构

 思路

我们的目的是寻找两个顺序相对一致的序列

所以我们需要构建一个二维数组进行统计,记录遍历的时候他们相同的地方

设有这样的两个序列:

{A,L,C,H,E,M,I,S,T}

{A,L,G,O,R,I,T,H,M,S}

 第一行的意思可以看作空和空.空和A。。。的最长子序列都为0

 从第二行开始,如果相同就将左上角的值复制加一填入,所以这里就填上   1(0+1)

 到第三个,元素不同的话就将这个位置的元素比较填入最大的,这里上是0左是1,所以填入1;

 以此类推

到了第二行,第二行与第二列元素一样,就读取左上角元素的复制后加一填入,1加1填入2

 之后都是以此类推,最后得到了

状态转移方程代码

 通过以上的分解后,我们可以得到这样一个状态转移方程

if(a===b){ //如果他们相同
table[row][col] = table[row - 1][co1 - 1] + 1; //我们找到他斜上角的那个值进行加一之后赋值
} else {
table[row][col] = Math. max(table[row][col - 1], table[row - 1][col]);//如果不相同,我们就填入元素上方和左边最大的元素
}

返回存储的序列信息

得到变化信息的数组后得出序列就很容易了

设置一个函数为lcs,对得到的变化信息进行处理

设储存每一次的信息,如果xi=yj则为1,另外两种情况为dis[][]中储存为2,3;如1代表两个都是一样的,2代表行是左,3代表列是上

 如果读取到为1的值说明在这里进行了作左上角的变化,说明他是一样的

使用递归即可完成

void LCS(int i, int j,char *A,int dis[100][100]){
  if (i == 0 || j == 0)
      return;
  if (dis[i][j] == 1){
      LCS(i - 1, j - 1,A,dis);
      printf("%c ", A[i-1]);
  }
  else if (dis[i][j] == 3){
      LCS(i - 1, j,A,dis);
  }else{
      LCS(i, j - 1,A,dis);
  }
}

最终代码实现

合并整理后的代码

#include<stdio.h>
#include<string.h>
char A[100] = "ABCBDAB";
char B[100] = "BDCABA";
int len[100][100];//最大子序列的生成矩阵
int dis[100][100]; //创建数组dis[][]来储存每一次的信息,如果xi=yj则为1,另外两种情况为dis[][]中储存为2,3;如1代表两个都是,2代表行是左,3代表列是上
 
void LCSLength(int n ,int m, char *x,char *y,int len[100][100],int dis[100][100]){
    int i, j, k;
    for (i = 0; i <= n; i++)
        len[i][0] = 0;//让列为0
    for (i = 0; i <= m; i++)
        len[0][i] = 0;//让第一行为0

    for (i = 1; i <= n; i++){
        for (j = 1; j <= m; j++){
            if (x[i-1] == y[j-1]){ //若他们相等,则让他等于左上角的元素加1
                len[i][j] = len[i - 1][j - 1] + 1;
                dis[i][j] = 1;      //记录下来在这里是相等的,标记为1
            }else if (len[i - 1][j] >= len[i][j - 1]){ //他们不相等的时候,取上和左元素最大的那个
                len[i][j] = len[i - 1][j];
                dis[i][j] = 3;//取上标记为3
            }else{
                len[i][j] = len[i][j - 1];
                dis[i][j] = 2;  //取左标记为2
            }
        }
    }
}
void LCS(int i, int j,char *A,int dis[100][100]){
  if (i == 0 || j == 0)//递归到第0行就返回
      return;
  if (dis[i][j] == 1){ //如果变化值为1就找再前一个变化值
      LCS(i - 1, j - 1,A,dis);
      printf("%c ", A[i-1]);//大于这个值,这里注意了,递归后,他是从最底层的递归开始打印,就是我们需要的顺序了
  }
  else if (dis[i][j] == 3){
      LCS(i - 1, j,A,dis);//变化值为3,说明了应该找上边的
  }else{
      LCS(i, j - 1,A,dis);//变化值为3,说明了应该找左边的
  }
}

int main()
{
int n = strlen(A);
int m = strlen(B);
printf("%d %d\n", n, m);//输出两个序列的长度
LCSLength(n,m,A,B,len,dis);
printf("最大子序列长度:%d ", len[n][m]); //最后的一个按直接的结果,他的值就是最长的序列数
int i,j;
for (i = 0; i <= n; i++){
    for (j = 0; j <= m; j++){
        printf("%d ", dis[i][j]);
    }
puts("");
}
puts("子序列为:");
LCS(n,m,A,dis);
}

 

得到的结果是

 

 

ENDING...

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  jTMfQq5cr55P   2024年05月17日   43   0   0 算法与数据结构
  jTMfQq5cr55P   2024年05月17日   40   0   0 算法与数据结构
ljGBcG8XAdp8
作者其他文章 更多