返回文章列表
algorithm2026年3月18日1 分钟阅读

题目描述

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,"ace" 是 "abcde" 的一个子序列,但 "aec" 不是 "abcde" 的一个子序列。

思路

本题是典型的动态规划问题,主要思路是使用二维数组dp,这里记录一下思考过程: 首先我们可以从后往前推导,最长的公共子序列其实是一个累积的过程,我们先想象有一个函数super可以计算出两个字符串的组最长公共子序列 这里有两种情况:

  1. text1和text2的最后一个字符相等,都为 e,那么可以得到:
super('ace', 'abcde') = super('ac', 'abcd') + 1
  1. text1和text2的最后一个字符不相等,那么要么是text1继续向前找,要么是text2继续向前找,我们又是需要「最大」,那么可以得到:
super('abc', 'abd') = max(super('ab', 'abd'), super('abc', 'ab'))

按照上面的逻辑,其实这就是一个递归过程。但是,一直切字符串太消耗内存了。 聪明一点的做法是:我们不传字符串,我们传索引(长度)。 设 i 是 text1 正在考察的长度,j 是 text2 正在考察的长度。 我们把上面的函数改写成 super(i, j),意思是:text1 的前 i 个字符和 text2 的前 j 个字符的匹配结果。 自然而然就想到了二维数组。 (鼓掌!)

因此,我们就可以得到动态规划的「状态转移方程」

dp[i][j] = dp[i-1][j-1] + 1 dp[i][j] = max(dp[i-1][j], dp[i][j-1])

涉及方法

  1. 动态规划 动态规划的核心是找到「状态转移方程」
  2. 二维数组 二维数组的使用可以让我们在计算过程中存储中间结果,避免重复计算,提高效率。
// 初始化一个 m x n 的二维数组 const dp = new Array(m).fill(0).map(() => new Array(n).fill(0)) // 或者使用 Array.from const dp = Array.from({ length: m }, () => new Array(n).fill(0));

关于Array.from的使用,可以参考 MDN 上的文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from

踩坑 “为什么不用 new Array(m).fill(new Array(n).fill(0)) ?” 因为 JavaScript 中的数组是引用类型。如果给 fill 传入一个数组实例,外层数组的所有项都会指向这个相同的内存地址。 修改其中一行的数据,其他行也会跟着改变,导致状态污染。所以我使用 Array.from 结合回调函数,确保每一行都是每次执行回调时 new 出来的独立数组实例。

实际处理

在实际的代码实现中,我们需要注意边界条件的处理,例如当 i 或 j 为 0 时,dp[i][j] 应该为 0,因为空字符串与任何字符串的最长公共子序列长度都是 0。 避免我们的字符串1和字符串2的长度为0或者首个字符相同的时候,访问dp[-1][-1]导致数组越界或者错误的结果。 因此我们这里需要给二维数组加一个边框:

const m = text1.length, n = text2.length const dp = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0))

ps:说好的算法日记,结果隔好久才写,忏悔忏悔。马上自律起来

© 2026 Blog Owner. All rights reserved.