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

文章目录

一. 力扣 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;
    }
}
相关推荐
weixin_6495556711 小时前
C语言程序设计第四版(何钦铭、颜晖)第十章函数与程序设计之汉诺塔问题
c语言·c++·算法
C羊驼11 小时前
C语言:随机数
c语言·开发语言·经验分享·笔记·算法
weisian15111 小时前
Java并发编程--17-阻塞队列BlockingQueue:生产者-消费者模式的最佳实践
java·阻塞队列·blockqueue
奔跑的呱呱牛11 小时前
GeoJSON 在大数据场景下为什么不够用?替代方案分析
java·大数据·servlet·gis·geojson
爱丽_12 小时前
Pinia 状态管理:模块化、持久化与“权限联动”落地
java·前端·spring
xushichao198912 小时前
实时数据压缩库
开发语言·c++·算法
minji...12 小时前
Linux 文件系统 (三) 软连接和硬链接
linux·运维·服务器·c++·算法
故事和你9112 小时前
sdut-python-实验四-python序列结构(21-27)
大数据·开发语言·数据结构·python·算法
memcpy012 小时前
LeetCode 1456. 定长子串中元音的最大数目【定长滑窗模板题】中等
算法·leetcode·职场和发展
liuyao_xianhui12 小时前
优选算法_模拟_提莫攻击_C++
开发语言·c++·算法·动态规划·哈希算法·散列表