子序列问题笔记——代码随想录学习(一)

图片来源:代码随想录https://leetcode-cn.com/problems/longest-palindromic-subsequence/solution/516-zui-chang-hui-wen-zi-xu-lie-dong-tai-hap0/

##「代码随想录」带你学透DP子序列问题!516. 最长回文子序列【动态规划】详解
在这里插入图片描述

子序列

最长递增子序列 (不连续)

在这里插入图片描述

var lengthOfLIS = function (nums) {
    const len = nums.length;
    let dp = new Array(len).fill(1);
    for (let i = 1; i < len; i++) {
        for (let j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
    }
    return Math.max.apply(null, dp);
};
let arr = [1, 3, 6, 7, 9, 4, 10, 5, 6];
lengthOfLIS(arr)

最长连续递增序列 (连续)

在这里插入图片描述

var findLengthOfLCIS = function(nums) {
    // dp[i] 表示以 i结尾最长连续递增序列的长度
    // base case dp[0] = 1
    // dp[i] = dp[i-1]+1 如果 nums[i-1]>nums[i]
    const dp = new Array(nums.length).fill(1);
    for(let i =1;i<nums.length;i++){
        if(nums[i-1]<nums[i]){
            dp[i]=dp[i-1]+1;
        }
    }
    return Math.max.apply(null,dp);
};

最长递增子序列的个数 (不连续)

在这里插入图片描述
定义dp:
length[i]以i结尾的最长递增序列的长度
count[i]以i结尾的最长递增序列的个数
初始值:
默认length[i] 最长长度,长度就是它自己,长度为1
默认count[i] 最长个数,个数默认1

   const len = nums.length;
    let length = new Array(len).fill(1); // length[i]以i结尾的最长递增序列的长度
    let count = new Array(len).fill(1); // count[i]以i结尾的最长递增序列的个数
    maxFun = () => {
        const max = 0;
        for (let i = 1; i < len; i++) {

            for (let j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    if (length[j] >= length[i]) {  // 前一个最大长度大于等于目标位置长度
                        length[i] = length[j] + 1;
                        count[i] = count[j];
                    } else if (length[j] + 1 == length[i]) {
                        count[i] += count[j];
                    }
                }
            }
        }
        return Math.max.apply(null, length)
    }
    let Maxlen = maxFun(nums), rs = 0;
    for (let i = 0, j = 0; i < length.length; i++) {
        if (length[i] == Maxlen) {
            rs += count[i];
        }
    }
    return rs

最长重复子数组 (连续)

在这里插入图片描述

var findLength = function(nums1, nums2) {
    const len1 = nums1.length, len2 = nums2.length;
    let dp = new Array(len1), max = 0;
    for (let i = 0; i < len1; i++) {
        dp[i] = new Array(len2).fill(0);
    }
    for (let i = 0; i < len1; i++) {
        for (let j = 0; j < len2; j++) {
            if (nums1[i] == nums2[j]) {
                if (i == 0 || j == 0) {
                    dp[i][j] = 1;
                } else {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                if (dp[i][j] > max) {
                    max = dp[i][j]
                }
            }
        }
    }
    return max;
};

最长公共子序列 (不连续)

在这里插入图片描述

var longestCommonSubsequence = function(text1, text2) {
        let dp = new Array(text1.length+1);
        for(let i=0;i<dp.length;i++){
            dp[i] = new Array(text2.length+1).fill(0);
        }
        for(let i=1;i<text1.length+1;i++){
            for(let j=1;j<text2.length+1;j++){
                if(text1[i-1]===text2[j-1]){
                    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[text1.length][text2.length]
};

不相交的线

实质是最长公共子序列 ,这是它的变式 (注意变式考法)
在这里插入图片描述


回文

回文子串 (回文连续)

在这里插入图片描述

// 动态规划 O(N^2)+O(N^2)
var countSubstrings = function (s) {
    const len = s.length;
    let rs = 0;
    const dp = new Array(len);
    for (let i = 0; i < dp.length; i++) {
        dp[i] = new Array(len).fill(false);
    }
    for (let j = 0; j < len; j++) { // 从上往下,从左往右
        for (let i = 0; i <= j; i++) {
            if (s[i] === s[j]) {
                if (j - i < 2) {    // 单个字母或者两个相同字母时
                    dp[i][j] = true;
                    rs++;
                } else if (dp[i + 1][j - 1]) { // 三个以上是回文字符串
                    dp[i][j] = true
                    rs++;
                }
            }
        }
    }
    return rs;
};

// 二分法 中心扩散 (数组中 每个元素就是一个中心(可以是一个元素,也可以是两个相同元素))
// O(N^2)+O(1)
var countSubstrings2 = function (s) {
    let rs = 0;
    // 已s的每一个元素为中心;
    for (let i = 0; i < s.length; i++) {
        rs += extend(i, i, s);           // 选择一个中心
        rs += extend(i, i + 1, s);       // 选择两个中心
    }
    function extend(i, j, s) {       // 已中心朝外扩散,看有多少个回文串。
        let res = 0;
        while (i >= 0 && j < s.length && s[i] == s[j]) {
            i--;
            j++;
            res++;
        }
        return res;
    }
    return rs;
};

最长回文子串 (回文连续)

在这里插入图片描述

var longestPalindrome = function (s) {
    const len = s.length;
    let max = 0, rs = "";
    const dp = new Array(len);
    for (let i = 0; i < len; i++) {
        dp[i] = new Array(len).fill(false);
    }
    for (let j = 0; j < len; j++) {
        for (let i = 0; i <= j; i++) {
            if (s[i] == s[j]) {
                if (j - i < 2) {    // 等于长度1或2的回文串
                    dp[i][j] = true;
                } else if (dp[i + 1][j - 1]) { // 长度为3的回文串
                    dp[i][j] = true;
                }
                if (dp[i][j] && max < j - i + 1) { //求最值
                    max = j - i + 1;
                    rs = s.slice(i, j + 1);
                }
            }
        }
    }
    return rs;
};
// 双指针 中心扩散
var longestPalindrome1 = function (s) {
    const len = s.length;
    let rs = '', max = 0;
    for (let i = 0; i < len; i++) {
        // 返回当前较长的回文串
        let tmp = Math.max(extend(i, i, s).length, extend(i, i + 1, s).length);
        // 记录给max
        max = max < tmp ? tmp : max;
        // max等于谁,在把结果给谁
        rs = max == extend(i, i, s).length ? extend(i, i, s) : rs
        rs = max == extend(i, i + 1, s).length ? extend(i, i + 1, s) : rs
    }
    function extend(i, j, s) {
        let res = '';
        while (i >= 0 && j < len && s[i] == s[j]) {
            res = s.slice(i, j + 1);
            i--;
            j++;
        }
        return res;
    }
    return rs;
}

最长回文子序列 (回文不连续)

在这里插入图片描述
最长回文子序列 ,因为回文串要比较两端的对称性,这里的dp数组应该考虑是二维数组。
s[i,j] 表示以【i,j】为范围的s字符串。dp[i][j] 表示长度范围是s[i,j] 时,最长回文子序列的长度。base case是斜对角线上的单个字母组成的回文串,长度为1。
状态转移方程:
dp[i][j] = dp[i+1][j-1] s[i]==s[j];

dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j]) s[i]!=s[j]

var longestPalindromeSubseq = function(s) {
    const dp = new Array(s.length), len = s.length;
    for (let i = 0; i < len; i++) {
        dp[i] = new Array(len).fill(0);
        dp[i][i] = 1;
    };
    // dp[i][j] = dp[i+1][j-1] s[i]==s[j];
    // dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j]) s[i]!=s[j]
    for (let i = len - 2; i >=0; i--) {
        for (let j = i + 1; j < len; j++) {
            if (s[i] == s[j]) {
                dp[i][j] = dp[i + 1][j - 1] + 2;
            } else {
                dp[i][j] = Math.max(dp[i][j - 1], dp[i+1][j]);
            }
        }
    }
    return dp[0][len-1];
};

编辑距离

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值