《算法导论》ʚ读书笔记&浅析ɞ 第十五章 - 最长公共子序列(包含js版代码实现)

在这里插入图片描述

背景简介

什么是子序列?

某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列

大概意思能在父序列串中按照对应的前后顺序找到的序列 就是子序列

举个例子

X {A,B,C,B,D,A,B}

Y {B,D,C,A,B,A}

DA 是x的子序列 AD不是

算法思路

假定我们称

  • 最长公共子序列是lcs
  • X的长度为m
  • Y的长度为n

当只有1个字符的时候,那么就是判断x1等于y1否 假定最终lcs b1
当有2个字符的时候,从后往前遍历 如果x2=y2 那么只要求之前的lcs再加上x2就是最终lcs 假定为b2
当有3个字符的时候,从后往前遍历 如果x3=y3 那么只要求b2+x3就是最终lcs b3

以此类推

再来看下 如果Xm!=Yn 那么就是求Xm-1,Yn Xm,Yn-1两种组合的最大lcs

假定c[i][j]表示i,j区间内的lcs长度

c[i][j] 如果x[i]=y[j] 那么c[i][j]=c[i-1][j-1]+1
如果x[i]!=y[j] 那么c[i][j]=max(c[i-1][j],c[j-1][i])

仅仅有这张表是不够的,我们还需要记录对应的查找顺序 也就是上述三种情况

  1. i-1,j-1的方向
  2. i-1,j的方向
  3. i,j-1的方向

算法过程

新建两张表
let b = newTable(m + 1, n + 1);:顺序表
let c = newTable(m + 1, n + 1);:长度表

遍历m,n长度所有的情况 然后判断三种情况就可以了

遍历

for (let i = 1; i < m + 1; i++){
    for (let j = 1; j < n + 1; j++) {
    }
}

3种情况

if (x[i - 1] == y[j - 1]) {
        c[i][j] = c[i - 1][j - 1] + 1;
        b[i][j] = "c";
      } 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] = "a";
      }

最后输出最长子序列 依然是递归查表就可以了

代码实现

//生成二维数组
function newTable(m, n) {
  let arr = [];
  for (let i = 0; i < m; i++) {
    let arrInside = [];
    for (let j = 0; j < n; j++) {
      arrInside.push(0);
    }
    arr.push(arrInside);
  }
  return arr;
}
// 生成序列表
function LcsLength(x, y) {
  let m = x.length;
  let n = y.length;
  let b = newTable(m + 1, n + 1);
  let c = newTable(m + 1, n + 1);
  for (let i = 1; i < m + 1; i++) {
    for (let j = 1; j < n + 1; j++) {
      if (x[i - 1] == y[j - 1]) {
        c[i][j] = c[i - 1][j - 1] + 1;
        b[i][j] = "c";
      } 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] = "a";
      }
    }
  }
  return { b, c };
}
//最长公共子序列
function getLcs(X, Y) {
  const b = LcsLength(X, Y).b;
  let str = "";
  function select(b, X, i, j) {
    if (i == 0 || j == 0) return;
    if (b[i][j] == "c") {
      select(b, X, i - 1, j - 1);
      str = str + X[i - 1];
    } else if (b[i][j] == "b") {
      select(b, X, i - 1, j);
    } else {
      select(b, X, i, j - 1);
    }
  }
  select(b, X, X.length, Y.length);
  return str;
}

module.exports = getLcs;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值