【动态规划】子数组系列(下)

这篇具有很好参考价值的文章主要介绍了【动态规划】子数组系列(下)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

子数组问题

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

【动态规划】子数组系列(下)

1. 等差数组划分

传送门:力扣413

题目:

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

1.1 题目解析

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

1.2 算法原理

1.2.1 状态表示

根据经验+题目要求:

  1. 题目给了一个一维数组,要求得到“等差数列”个数
    • 一维dp表,大小为n
  2. 以…为结尾,去研究
    • 子数组问题常见就是“以…为结尾的子数组”集合

故得到状态表示:

dp[i]表示以i为结尾的子数组中,等差数组的个数~
【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

1.2.2 状态转移方程

等差数组的特性:公差一致

  • a、b、c为等差数列,b、c、d为等差数列,那么a、b、c、d也为等差数列!

对于[i]这个位置:
【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

如果b + dp[i] == 2 * c,那么nums[i]为结尾的子数组至少有b、c、dp[i]这一个等差数列

  • 如果c为结尾的子数组有等差数列,那么a、b、c、nums[i]等等…数列都是等差数列,即c为结尾的子数组中的等差数列继承给nums[i]

如果b + dp[i] != 2 * c,那么nums[i]为结尾的子数组一个等差数列都没有~

故得到状态转移方程:

dp[i] = nums[i - 2] + nums[i] == nums[i - 1] * 2 ? 1 + dp[i - 1] : 0;

1.2.3 初始化

这里用到dp[i - 1]所以需要处理dp[0],而实际上dp[0]和dp[1]是绝对为0的,因为等差数列至少要3个元素

所以不必理会~

1.2.4 填表顺序

从左往右依次填(从2开始)

1.2.5 返回值

本题不是以dp表的某个值为返回值,而是dp表值的总和,dp表每个元素是符合要的子数组数,而返回值即是符合要求的子数组的总和~

1.3 代码实现

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        //1. 创建dp表
        //2. 初始化
        //3. 填表顺序
        //4. 返回值
        int n = nums.length;
        int[] dp = new int[n];
        int sum = 0;
        for(int i = 2; i < n; i++) {
            dp[i] = nums[i - 2] + nums[i] == 2 * nums[i - 1] ? 1 + dp[i - 1] : 0;
            sum += dp[i];
        }
        return sum;
    }
}

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

2. 最长湍流子数组

传送门:力扣978

题目:

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

2.1 题目解析

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

2.2 算法原理

2.2.1 状态表示

“经验+题目要求”快速得到一维dp表,dp[i]代表以i为结尾的子数组中,是湍流子数组的最长长度

而这样的状态,不太细,至少我们刚才划分出了两种情况,那么我联系等一下的状态转移方程,我们不知道dp[i - 1]是下面的几种情况:

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

对于相等的情况,dp值必为1,因为相等为结尾的子数组,必然不为湍流子数组

有f[i]表示,以i为结尾的子数组,末尾为上升的湍流子数组的最长长度

g[i]表示,以i为结尾的子数组,末尾为下降的湍流子数组的最长长度

疑问:可以代码判断其前面是哪种情况啊~

没错,但是我们每次都要选择其中的较大值,那么另一种情况就被我们放弃了,而这是不一定的,“另一个情况”可能是“更长湍流子数组的起源”

2.2.2 状态转移方程

结合刚才的分析有,以i为结尾的子数组最后一段为上升还是下降的判断:

如果arr[i - 1] < arr[i],即上升

  • f[i] = g[i - 1] + 1
  • g[i] = 1

对于f表,找到i - 1 下降子数组的最长长度,然后续上去

如果arr[i - 1] < arr[i],即下降

  • f[i] = 1
  • g[i] = f[i - 1] + 1

对于g表,找到i - 1 上升子数组的最长长度,然后续上去

如果arr[i - 1] == arr[i],即相等

  • f[i] = 1
  • g[i] = 1
    【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法
2.2.3 初始化

dp[i]都大于等于1,dp[0]显然是1~

初始化f表g表每个元素为1

2.2.4 填表顺序

从左往右依次填

2.2.5 返回值

dp表中的最大值

2.3 代码实现

class Solution {
    public int maxTurbulenceSize(int[] arr) {
        //1. 创建dp表
        //2. 初始化
        //3. 填表
        //4. 返回值
        int n = arr.length;
        int max = 1;
        int[] f = new int[n];
        int[] g = new int[n];
        Arrays.fill(f, 1);
        Arrays.fill(g, 1);
        for(int i = 1; i < n; i++) {
            if(arr[i] > arr[i - 1]) {
                f[i] += g[i - 1];
            }else if(arr[i] < arr[i - 1]) {
                g[i] += f[i - 1];
            }   
            max = Math.max(max, Math.max(f[i], g[i]));
        }
        return max;
    }
}

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

3. 单词划分

传送门:力扣139

题目:

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

3.1 题目解析

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

3.2 算法原理

3.2.1 状态表示

“经验+题目要求”可迅速得到,一维dp表(大小为n)

dp[i] 表示 [0, i]这段字符串,是否可以有字典的单词拼接成

3.2.2 状态转移方程

增加第i这个字符后能被成功拼接就要有下面这个判断:
【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

而我们只需要从右往左去判断紫色部分能否构成一个单词并且蓝色部分可以被拼接即可,因为找到两个单词,也在蓝色部分的之前判断里判断过了,所以没有必要~

即找到一个j

  • 使得[0, j - 1]可以被单词拼接 => dp[j - 1]
  • 使得[j, i]是一个单词 => contains(substring(j, i + 1))(伪代码)

得到状态转移方程:

if(set.contains(s.substring(0, i + 1))) {
    dp[i] = true;
}else {
    for(int j = i; j > 0; j--) {
        if(dp[j - 1] && set.contains(s.substring(j, i + 1))) {
            dp[i] = true;
            break;
        }
    }
}
3.2.3 初始化

并没有什么越界问题,java布尔数组默认值为false

  • 可以通过一个假数据,去简化代码,但是要考虑下标对应啥的,太麻烦了~
3.2.4 填表顺序

从左往右填表

3.2.5 返回值

返回最后一个元素的值

3.3 代码实现

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        //1. 创建dp表
        //2. 初始化
        //3. 填表
        //4. 返回值
        char[] string = s.toCharArray();
        int n = string.length;
        boolean[] dp = new boolean[n];
        Set<String> set = new HashSet(wordDict);
        for(int i = 0; i < n; i++) {
            if(set.contains(s.substring(0, i + 1))) {
                dp[i] = true;
            }else {
                for(int j = i; j > 0; j--) {
                    if(dp[j - 1] && set.contains(s.substring(j, i + 1))) {
                        dp[i] = true;
                        break;
                    }
                }
            }
        }
        return dp[n - 1];
    }
}

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

时间复杂度为O(N2

空间复杂度为O(N + M)

  • M为单词数

4. 环绕字符串中唯一的子字符串

传送门:力扣467

题目:

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

4.1 题目解析

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

  • 并且,满足要求的子串不能重复~

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

4.2 算法原理

4.2.1 状态表示

由“经验 + 题目要求”可以迅速得到一维dp表(大小为n)

dp[i]表示以i为结尾的子串中,属于base中的子串个数

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

4.2.2 状态转移方程

分为两种情况:

  1. 子串大小为1,dp[i] = 1(一个字母必然出现)
  2. 子串大小大于1
    1. string[i]这个字母能续上去,那么就继承dp[i - 1]
    2. 不能续上,就为0

故得到状态转移方程:

dp[i] = 1 + string[i] == string[i - 1] + 1 || (string[i] == ‘a’ && string[i - 1] == ‘z’) ? dp[i - 1] : 0;

4.2.3 初始化

初始化dp表每个值为1,方便计算,并且dp表的元素最小值就为1

4.2.4 填表顺序

从左往右填表

4.2.5 返回值

这里直接返回dp表总和?

  • 很显然是不对的!

因为这里可能会出现大量的重复子串!

如何去重?

  • 不难想到,重复的子串的特点就是,以同一个字母为结尾
    【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法

返回26个字母对应的dp值之和

4.3 代码实现

class Solution {
    public int findSubstringInWraproundString(String s) {
        //1. 创建dp表
        //2. 初始化
        //3. 填表
        //4. 返回值
        int n = s.length();
        char[] string = s.toCharArray();
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        for(int i = 1; i < n; i++) {
            dp[i] += string[i] == string[i - 1] + 1 || 
                (string[i] == 'a' && string[i - 1] == 'z') ? dp[i - 1] : 0;
        }
        int[] count = new int[26];
        for(int i = 0; i < n; i++) {
            int index = string[i] - 'a';
            count[index] = Math.max(count[index], dp[i]);
        }
        int sum = 0;
        for(int i = 0; i < 26; i++) {
            sum += count[i];
        }
        return sum;
    }
}

【动态规划】子数组系列(下),剑指offer与算法,动态规划,算法


文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

这就是子数组系列的内容了~

代码位置:DP06 · 游离态/马拉圈2023年7月 - 码云 - 开源中国 (gitee.com)文章来源地址https://www.toymoban.com/news/detail-608530.html


到了这里,关于【动态规划】子数组系列(下)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 剑指offer(C++)-JZ46:把数字翻译成字符串(算法-动态规划)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 有一种将字母编码成数字的方式:\\\'a\\\'-1, \\\'b-2\\\', ... , \\\'z-26\\\'。 现在给一串数字,返回有多少种可能的译码结果 数据范围:字符串长度满足 0n≤90 进阶:空间复杂度

    2024年02月07日
    浏览(47)
  • 剑指offer(C++)-JZ48:最长不含重复字符的子字符串(算法-动态规划)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。 数据范围:  s.length≤40000 s.length≤40000 示例: 输入: 返回值: 说明

    2024年02月06日
    浏览(59)
  • 剑指offer:动态规划

    JZ42 连续子数组的最大和(一) 简单 通过率:40.77% 时间限制:1秒 空间限制:64M 知识点动态规划贪心 描述 输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。 数据范围: 1=n=2×105 −100=a[i]=100 要

    2024年02月03日
    浏览(38)
  • Java 动态规划 剑指 Offer 47. 礼物的最大价值

     代码展示:         进行动态规划的五个步骤:         1.状态表示                 dp[i][j]表示从起点到[i][j]这个位置上能获得礼物的最大价值         2.状态转移方程                 我们分析距离[i][j]最近的位置,根据题意我们到达[i][j]位置只能从[i-1][j]向下移动或

    2024年02月13日
    浏览(41)
  • 剑指offer中算法:二维数组中的查找

    在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 示例 现有矩阵 matrix 如下: { {1, 4, 7}, {2, 5, 8,}, {3, 6, 9} } 给定 target = 9,

    2024年02月12日
    浏览(44)
  • (动态规划) 剑指 Offer 60. n个骰子的点数 ——【Leetcode每日一题】

    难度:中等 把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s 。输入 n ,打印出s的所有可能的值出现的概率。 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。 示例 1: 输入: 1 输出: [0.16667,0.16667,0.16667,

    2024年02月11日
    浏览(45)
  • 算法沉淀——动态规划篇(子数组系列问题(下))

    几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此 1.、状态表示:通常状态表示分为以下两种,其中更是第一种为主。 以i为结尾 ,dp[i] 表示什么,通常为代求问题(具体依题目而定) 以i为开始 ,dp[i]表示什么,通常为代求问题(具体依题目而

    2024年04月14日
    浏览(52)
  • (动态规划) 剑指 Offer 10- I. 斐波那契数列 ——【Leetcode每日一题】

    难度:简单 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N) )。斐波那契数列的定义如下: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N 1. 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 答案需要取模 1e9+7(1000000007),如计

    2024年02月12日
    浏览(56)
  • 【LeetCode: 剑指 Offer II 089. 房屋偷盗(打家窃舍) | 暴力递归=>记忆化搜索=>动态规划】

    🍎作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎 🍎座右铭:人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯🎯 剑指 Offer II 089. 房屋偷盗

    2024年02月02日
    浏览(46)
  • 【LeetCode: 剑指 Offer 60. n个骰子的点数 | 数学+ 暴力递归=>记忆化搜索=>动态规划】

    🍎作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享💎💎💎 🍎座右铭:人生如棋,我愿为卒,行动虽慢,可谁曾见我后退一步?🎯🎯🎯 剑指 Offer 60. n个骰子的点

    2023年04月19日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包