算法奇妙屋(十九)-子序列问题(动态规划)

文章目录

一. 力扣 300. 最长递增子序列

1. 题目解析

2. 算法原理

3. 代码

java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        // 初始化dp表
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }
        // 填表
        int ret = 1;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                    ret = Math.max(ret, dp[i]);
                }
            }
        }
        return ret;
    }
}

二. 力扣 376. 摆动序列

1. 题目解析

这道题和上一个专题子数组系列(动态规划)的(湍流子数组十分相似), 可以先看那道题, 再看这道题的时候思路就十分清晰了

2. 算法原理

写这道题的图解时, 思路更清晰了, 对于子序列问题有了进一步的认识

3. 代码

java 复制代码
class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int[] g = new int[n];
        // 初始化
        for (int i = 0; i < n; i++) {
            f[i] = g[i] = 1;
        }
        // 填表
        int ret = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    f[i] = Math.max(f[i], g[j] + 1);
                }else if (nums[i] < nums[j]) {
                    g[i] = Math.max(g[i], f[j] + 1);
                }
                ret = Math.max(ret, Math.max(f[i], g[i]));
            }
        }
        return ret;
    }
}

三. 力扣 673. 最长递增子序列的个数

1. 题目解析

这道题相当于最长递增子序列长度的一个进阶, 先看完上面那道题, 再看这道题更轻松

2. 算法原理

首先先引入一个贪心技巧, 既要又要


利用上方贪心技巧来做这道题

3. 代码

java 复制代码
class Solution {
    public int findNumberOfLIS(int[] nums) {
        int n = nums.length;
        int[] len = new int[n];
        int[] count = new int[n];
        // 初始化
        for (int i = 0; i < n; i++) {
            len[i] = count[i] = 1;
        }
        // 填表, 并求最大个数
        int max = len[0];
        int ret = count[0];
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    if (len[j] + 1 == len[i]) {
                        count[i] += count[j];
                    } else if (len[j] + 1 > len[i]) {
                        len[i] = len[j] + 1;
                        count[i] = count[j];
                    }
                }
            }
            if (len[i] == max) {
                ret += count[i];
            } else if (len[i] > max) {
                max = len[i];
                ret = count[i];
            }
        }
        return ret;
    }
}

四. 力扣 646. 最长数对链

1. 题目解析

这道题乍一看有点无头绪,但当我们画图,就豁然开朗了, 这道题和最长递增子序列十分相似

2. 算法原理

3. 代码

java 复制代码
class Solution {
    public int findLongestChain(int[][] pairs) {
        // 排序
        Arrays.sort(pairs, (a, b) -> a[0] - b[0]);
        // 创建dp表
        int n = pairs.length;
        int[] dp = new int[n];
        // 初始化dp表
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }
        // 填表
        int ret = 1;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (pairs[j][1] < pairs[i][0]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                ret = Math.max(ret, dp[i]);
            }
        }
        return ret;
    }
}

五. 力扣 1218. 最长定差子序列

1. 题目解析

2. 算法原理

3. 代码

java 复制代码
class Solution {
    public int longestSubsequence(int[] arr, int difference) {
        // 建立hash表
        int n = arr.length;
        HashMap<Integer,Integer> hash = new HashMap<>();
        // 初始化
        hash.put(arr[0], 1);
        // 填表
        int ret = 1;
        for (int i = 1; i < n; i++) {
            int a = arr[i];
            int b = a - difference;
            int dp = hash.getOrDefault(b, 0) + 1;
            hash.put(a, dp);
            ret = Math.max(ret, dp);
        }
        return ret;
    }
}

六. 力扣 873. 最长的斐波那契子序列的长度

1. 题目解析

斐波那契数列我们很熟悉, 但是斐波那契+子序列的组合你见过吗?

2. 算法原理

这道题是一个二维dp表, 很有意思的一道题, 只用到一半空间

3. 代码

java 复制代码
class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        // 创建dp, hash表
        int n = arr.length;
        int[][] dp = new int[n][n];
        HashMap<Integer, Integer> hash = new HashMap<>();
        // 初始化dp, hash
        for (int i = 0; i < n; i++) {
            hash.put(arr[i], i);
            for (int j = i + 1; j < n; j++) {
                dp[i][j] = 2;
            }
        }
        // 填表
        int ret = 2;
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int a = arr[j] - arr[i];
                if (hash.containsKey(a) && a < arr[i]) {
                    dp[i][j] = dp[hash.get(a)][i] + 1;
                    ret = Math.max(ret, dp[i][j]);
                }
            }
        }
        return ret == 2 ? 0 : ret;
    }
}

七. 力扣 1027. 最长等差数列

1. 题目解析

这道题和最长定差子序列有很多相同点

2. 算法原理

这道题依旧是二维dp表, 具体的分析过程都在下面图解中

3. 代码

java 复制代码
class Solution {
    public int longestArithSeqLength(int[] nums) {
        // 建表
        int n = nums.length;
        int[][] dp = new int[n][n];
        HashMap<Integer, Integer> hash = new HashMap<>();
        // 初始化
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                dp[i][j] = 2;
            }
        }
        hash.put(nums[0], 0);
        // 填表
        int ret = 2;
        for (int i = 1; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int a = nums[i] * 2 - nums[j];
                if (hash.containsKey(a)) {
                    dp[i][j] = dp[hash.get(a)][i] + 1;
                    ret = Math.max(ret, dp[i][j]);
                }
            }
            hash.put(nums[i], i);
        }
        return ret;
    }
}

八. 力扣 446. 等差数列划分 II - 子序列

1. 题目解析

这道题题意不难理解, 写代码时细节方面较多

2. 算法原理

这里我们依旧采取第二种填表顺序, 时间上更优秀, 但是细节方面较多, 尤其是计算过程会越界, 花了小编2个小时画图和调试

3. 代码

java 复制代码
class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        // 建表,初始化
        int n = nums.length;
        int[][] dp = new int[n][n];
        Map<Long, List<Integer>> hash = new HashMap<>();
        hash.put((long)nums[0], new ArrayList<>());
        hash.get((long)nums[0]).add(0);
        // 填表
        int ret = 0;
        for (int i = 1; i < n; i++) {
            long b = (long)nums[i];
            for (int j = i + 1; j < n; j++) {
                long c = (long) nums[j];
                long a = 2L * b - c;
                // 这里a一定在b的左边, 不用多余判断
                if (hash.containsKey(a)) {
                    for (int k : hash.get(a)) {
                        dp[i][j] += dp[k][i] + 1;
                    }
                }
                ret += dp[i][j];
            }
            if (!hash.containsKey(b)) {
                hash.put(b, new ArrayList<>());
            }
            hash.get(b).add(i);
        }
        return ret;
    }
}
相关推荐
im_AMBER2 小时前
Leetcode 80 统计一个数组中好对子的数目
数据结构·c++·笔记·学习·算法·leetcode
小满、2 小时前
RabbitMQ:AMQP 原理、Spring AMQP 实战与 Work Queue 模型
java·rabbitmq·java-rabbitmq·spring amqp·amqp 协议·work queue
萧曵 丶2 小时前
Java Stream 实际用法详解
java·stream·lambda
dvlinker2 小时前
动态代理技术实战测评—高效解锁Zillow房价历史
android·java·数据库
喵手2 小时前
JVM 基础知识:深入理解 Java 的运行时环境!
java·jvm·jvm基础·java运行环境
简烦2 小时前
外层事务的 afterCommit 中调用内层事务方法时,内层事务的 TransactionSynchronization 注册失败 / 不执行
java·spring
峥嵘life2 小时前
Android16 EDLA 认证BTS测试Failed解决总结
android·java·linux·运维·学习
wniuniu_2 小时前
object->osd
android·java·数据库
猫头虎2 小时前
IntelliJ IDEA 2025.3 最新变化:值得更新吗?
java·开发语言·ide·人工智能·intellij-idea·idea·gitcode