「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
最长公共子序列是一道典型的二维动态规划的问题。
假设字符串text1和text2的长度分别为m和n,那么我们就创建一个m+1行n+1列的二维数组dp,其中dp[i][j]表示text1[0:i]和text2[0:j]的最长公共子序列。
即text1从0到i的子序列和text从0到j的子序列的最长公共子序列。
动态规划在我的理解就是将一个大的问题分成最小单元的子问题,进行累加对比。然后再通过上一次对比的值和这一次对比的值,计算出此时对比的最终值。就拿这道最长公共子序列为例。
text1 = "abcde", text2 = "ace";
此时我们会建立一个二维数组,行列分别为text1.length+1,text2.length+1,dp:
[0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0]
我们将text1进行循环,得到了其最小单元
for(let i = 1;i<=m;i++){
let c1 = text1[i-1]
}
由于当i=0的时候,为我们设置的初始值0,代表空字符串情况下,和任何字符串对比,最长公共子序列都是零。
所以这就是为什么长度为m+1,所以我们从i=1,开始遍历,到遍历完m即m+1为止。
若要能获取到text1的第i位,则需要text1[i-1]。
第二步则是对text2进行遍历,和c1进行对比。
for(let i = 1;i<=m;i++){
let c1 = text1[i-1]
for(let j = 1;j<=n;j++){
let c2 = text2[j-1]
}
}
将两个字符串都进行的最小化的遍历之后,就到了对比环节。
for (let i = 1; i <= m; i++) {
const c1 = text1[i - 1];
for (let j = 1; j <= n; j++) {
const c2 = text2[j - 1];
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1] + 1
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j])
}
}
}
如果c1=c2,那我们只需要在上次对比的结果上再加一即可,比如当text1和text2的第一个值相等,那么dp[1][1] = 1。
如果c1≠c2,那么就需要找到dp[i][j - 1], dp[i - 1][j]中最大的那一个,这两个值代表的是长度i-1的text1和长度j的text2进行对比的最大值与长度i的text1和长度j-1的text2的最大值中的最大值,代表现阶段的最大值,以text1 = "abcde", text2 = "ace"为例,假如此时i=3,j=3 此时的c1 = c,c2 = e,二者不相等,那就要对比i-1和j长度即"ab"和"ace"的长度1与i和j-1长度即"abc"和"ac"的长度2,中的最大值2。dp[3][3]=2。当text1和text2都遍历完,最后的dp[m][n]的值则为最终结果。
以下图为例
如果每次对比的结果相等,就在左上方的值加一,如果不等,就在左边和上边中选最大值进行赋值,知道二维数组全部遍历完毕,右下角的值为最终结果。
全部分析完毕后,最终代码为
var longestCommonSubsequence = function(text1, text2) {
const m = text1.length, n = text2.length;
const dp = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
const c1 = text1[i - 1];
for (let j = 1; j <= n; j++) {
const c2 = text2[j - 1];
if (c1 === c2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
};