力扣打卡 05.13
题目1143.最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
- 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
思路:
这是经典的动态规划问题,对于两个字符串可以使用两个指针分别在两个字符串上进行移动。有两种思路去解决:
思路一:自底向上。dp(i, j)
函数的作用就是计算以text1[i]和text[j]结尾的两个字符串的最长公共子序列,那么状态转移方程就是 dp(i,j)=Math.max(dp(i-1,j),dp(i,j-1),1+dp(i-1,j-1))
,其中1+dp(i-1,j-1)
当且仅当text1[i]===text[j]
时才存在,因此最后的结果就是dp(text1.length, text2.length)
。
思路二:自顶向下(递归)。dp(i, j)
函数的作用就是计算以text1[i]和text[j]开头的两个字符串的最长公共子序列,那么状态转移方程就是 dp(i,j)=Math.max(dp(i+1,j),dp(i,j+1),1+dp(i+1,j+1))
,其中1+dp(i+1,j+1)
当且仅当text1[i]===text[j]
时才存在,因此最后的结果就是dp(0,0)
。
**注意:**当我们使用自顶向下也就是递归的方法时,会产生很多冗余的计算,因此需要使用额外空间存储已经计算好的值,也就是“备忘录”,在这道题中备忘录就是memo[i][j]
,大小为text1.length*text2.length
。
//自底向上
var longestCommonSubsequence = function(text1, text2) {
let l1 = text1.length, l2 = text2.length;
let memo = [];
for(let i=0; i<=l1; i++){
memo.push(new Array(l2+1).fill(0));
}
for(let i=1; i<=l1; i++){
for(let j=1; j<=l2; j++){
if(text1[i-1] === text2[j-1]){
memo[i][j] = memo[i-1][j-1]+1;
}
memo[i][j] = Math.max(memo[i][j], memo[i-1][j], memo[i][j-1]);
}
}
return memo[l1][l2];
};