BluePen BluePen
  • Jenkins
  • OS
  • 算法
随笔
分类
标签
归档
关于
留言板
GitHub (opens new window)

Alex

一个好人
  • Jenkins
  • OS
  • 算法
随笔
分类
标签
归档
关于
留言板
GitHub (opens new window)
  • leetcode

    • 哈希

    • 字符串操作

    • 数字操作

    • 双指针

    • 递归

    • 链表

    • 动态规划

      • 最长上升子序列
      • 最长公共子序列
        • 题目描述
        • 解法
        • 总结
        • 参考
      • 最长回文子串
    • 二分法

  • interview

  • well_known_algo

  • algo
  • leetcode
  • 动态规划
Alex
2019-09-24
目录

最长公共子序列

-> 题目描述

Longest Common Subsequence

给定两个字符串 text1和 text2, 返回他们的最长公共子序列的长度。
子序列的定义:从源字符串中产生,可以与源字符串相同,也可以是只删除一些字符,剩下字符保持顺序不变产生的新字符串。
eg:"ace" 是 "abcde"的子序列,但 "aec" 不是。
公共子序列: 即两个字符串含有的相同的子序列

如果不存在公共子序列不存在, 返回 0

eg:

Input: text1 = "abcde", text2 = "ace" 
Output: 3  
Explanation: The longest common subsequence is "ace" and its length is 3.
1
2
3
Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.
1
2
3
Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.
1
2
3
Input: text1 = "ghbrgc", text2 = "hcbgcrcbh"
Output: 4
Explanation: The longest common subsequence is "hbgc" and its length is 4.
1
2
3

另:

  • 输入字符串均为英文小写字符
  • 字符串长度区间满足 [1, 1000]

From leetcode No.1143 Medium (opens new window)

/**
 * @param {string} text1
 * @param {string} text2
 * @return {number}
 */
var longestCommonSubsequence = function(text1, text2) {
    
};
1
2
3
4
5
6
7
8

-> 解法

点击查看
/**
 * @param {string} text1
 * @param {string} text2
 * @return {number}
 */
var longestCommonSubsequence = function (text1, text2) {
    const len1 = text1.length
    const len2 = text2.length

    const dp = new Array(len1 + 1)
    dp[0] = new Array(len2 + 1).fill(0)

    for (let i = 0; i < len1; i++) {
        dp[i+1] = new Array(len2 + 1).fill(0)
        for (let j = 0; j < len2; j++) {
            if (text1[i] === text2[j])
                dp[i+1][j+1] = dp[i][j] +1
            else
                dp[i+1][j+1] = Math.max(dp[i][j+1], dp[i+1][j])
        }
    }
    return dp[len1][len2]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

-> 总结

还是先找规律,确定状态转移方程
eg: 以text1, text2 均为abc为例
dp为一个二维数组,dp[i][j] 表示 text1的 [0, i]个元素 与text2的 [0, j]个元素的LCS (longestCommonSubsequence)
eg: dp[1][2] 及表示 ab 与 abc的 LCS, 对应的值为 2

所有情况具体为下图

a b c
a 1 1 1
b 1 2 2
c 1 2 3

可以得到:

  • dp[i][j]的最小值为 0
  • 当 text1[i] == text2[j]时, dp[i][j] = dp[i-1][j-1] + 1
  • 当 text1[i] != text2[j]时, dp[i][j] = Math.max(dp[i-1][j], dp[1][j-1])

由此便可得出递归规律

var longestCommonSubsequence = function (text1, text2) {
    const len1 = text1.length
    const len2 = text2.length
    function lcs(len1, len2) {
        if (len1 === -1 || len2 === -1) //原始状态
            return 0
        if (text1[len1] === text2[len2])
            return lcs(len1 - 1, len2 - 1) + 1
        else
            return Math.max(lcs(len1 - 1, len2), lcs(len1, len2 - 1))
    }
    return lcs(len1 - 1, len2 - 1)
};
1
2
3
4
5
6
7
8
9
10
11
12
13

但是递归调用栈次数太多,存在重复计算,于是可以用动态规划空间换时间的方案

学习提交中最快的解法:

点击查看
var longestCommonSubsequence = function(text1, text2) {
    const n1 = text1.length;
    const n2 = text2.length;
    
    let dp = new Array(n2 + 1).fill(0);
    let pre = 0;
    
    for (let i = 1; i <= n1; ++i) {
        pre = 0;
        for (let j = 1; j <= n2; ++j) {
            const tmp = dp[j];
            if (text1[i - 1] == text2[j - 1]) {
                dp[j] = pre + 1;
            } else {
                dp[j] = Math.max(dp[j - 1], dp[j]);   
            }
            pre = tmp;
        }
    }
    return dp[n2];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 时间复杂度不变,但是空间复杂度大大降低,且 dp的思想更加纯粹

-> 参考

  • longest-common-subsequence (opens new window)
编辑此页 (opens new window)
更新于: 2019-09-24 11:36
最长上升子序列
最长回文子串

← 最长上升子序列 最长回文子串→

Copyright © 2019-2022 | yxxy | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式