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

news2024/12/23 17:16:01

子数组问题

在这里插入图片描述

文章目录

  • 【动态规划】子数组系列(下)
    • 1. 等差数组划分
      • 1.1 题目解析
      • 1.2 算法原理
        • 1.2.1 状态表示
        • 1.2.2 状态转移方程
        • 1.2.3 初始化
        • 1.2.4 填表顺序
        • 1.2.5 返回值
      • 1.3 代码实现
    • 2. 最长湍流子数组
      • 2.1 题目解析
      • 2.2 算法原理
        • 2.2.1 状态表示
        • 2.2.2 状态转移方程
        • 2.2.3 初始化
        • 2.2.4 填表顺序
        • 2.2.5 返回值
      • 2.3 代码实现
    • 3. 单词划分
      • 3.1 题目解析
      • 3.2 算法原理
        • 3.2.1 状态表示
        • 3.2.2 状态转移方程
        • 3.2.3 初始化
        • 3.2.4 填表顺序
        • 3.2.5 返回值
      • 3.3 代码实现
    • 4. 环绕字符串中唯一的子字符串
      • 4.1 题目解析
      • 4.2 算法原理
        • 4.2.1 状态表示
        • 4.2.2 状态转移方程
        • 4.2.3 初始化
        • 4.2.4 填表顺序
        • 4.2.5 返回值
      • 4.3 代码实现

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

1. 等差数组划分

传送门:力扣413

题目:

在这里插入图片描述

1.1 题目解析

在这里插入图片描述

1.2 算法原理

1.2.1 状态表示

根据经验+题目要求:

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

故得到状态表示:

dp[i]表示以i为结尾的子数组中,等差数组的个数~
在这里插入图片描述

1.2.2 状态转移方程

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

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

对于[i]这个位置:
在这里插入图片描述

如果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;
    }
}

在这里插入图片描述

2. 最长湍流子数组

传送门:力扣978

题目:

在这里插入图片描述

2.1 题目解析

在这里插入图片描述

2.2 算法原理

2.2.1 状态表示

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

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

在这里插入图片描述

对于相等的情况,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
    在这里插入图片描述

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;
    }
}

在这里插入图片描述

3. 单词划分

传送门:力扣139

题目:

在这里插入图片描述

3.1 题目解析

在这里插入图片描述

3.2 算法原理

3.2.1 状态表示

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

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

3.2.2 状态转移方程

增加第i这个字符后能被成功拼接就要有下面这个判断:
在这里插入图片描述

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

即找到一个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];
    }
}

在这里插入图片描述

时间复杂度为O(N2

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

  • M为单词数

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

传送门:力扣467

题目:

在这里插入图片描述

4.1 题目解析

在这里插入图片描述

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

在这里插入图片描述

4.2 算法原理

4.2.1 状态表示

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

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

在这里插入图片描述

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表总和?

  • 很显然是不对的!

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

如何去重?

  • 不难想到,重复的子串的特点就是,以同一个字母为结尾
    在这里插入图片描述

返回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;
    }
}

在这里插入图片描述


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

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

代码位置:DP06 · 游离态/马拉圈2023年7月 - 码云 - 开源中国 (gitee.com)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初学spring5(五)使用注解开发

学习回顾&#xff1a;初学spring5&#xff08;四&#xff09;自动装配 一、使用注解开发 二、说明 在spring4之后&#xff0c;想要使用注解形式&#xff0c;必须得要引入aop的包 在配置文件当中&#xff0c;还得要引入一个context约束 <beans xmlns"http://www.sprin…

Node.js模块化加载机制

优先从缓存中加载 模块在第一次加载后会被缓存。这也意味着多次调用 require() 不会导致模块的代码被执行多次 注意:不论是内置模块、用户自定义模块、还是第三方模块&#xff0c;它们都会优先从缓存中加载&#xff0c;从而提高模块的加载效率 $就像下方图中测试 内置模块…

【软件测试】MySQL操作数据表常用sql语句(汇总)

目录&#xff1a;导读 前言 一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 数据表有哪些操作…

【JavaEE初阶】HTML

摄影分享~ 文章目录 一.第一个HTML程序1.创建一个HTML文件并运行2.在vscode中创建HTML文件并运行HTML代码的特点 二.HTML中的标签1.注释标签2.标题标签3.段落标签4.换行标签5.格式化标签6.图片标签&#xff1a;img7.超链接标签8.表格标签9.列表标签10.from标签input标签selec…

Tomcat概念及部署

一.Tomcat的概述 1.Tomcat介绍 &#xff08;1&#xff09;免费的、开放源代码的web应用服务器。 &#xff08;2&#xff09;主要处理的是动态页面&#xff08;做一个运行后端的程序&#xff09;可以处理静态页面&#xff0c;处理效果不及apache和nginx。 &#xff08;3&…

类变量和类方法的基本使用

什么是类变量 类变量也叫静态变量/静态属性&#xff0c;是该类的所有对象共享的变量&#xff0c;任何一个该类的对象访问它时&#xff0c;取到的都是相同的值&#xff0c;同样任何一个该类的对象去修改它时&#xff0c;修改的也是同一个变量。 如何定义类变量 定义语法&…

实战线性回归模型

引言 线性回归是一种最简单、也是最常用的预测模型&#xff0c;主要用于处理自变量和因变量之间的线性关系。举个例子&#xff0c;假设你是一名大学生&#xff0c;正在为你的经济学课程做一个研究项目&#xff0c;你想要知道大学生的学习时间和GPA&#xff08;绩点平均分&…

React 搭建DvaJS开发环境

那么 后面我们就开始将DvaJS了 他是一个特别优秀的React轻量级应用框架 他的使用了非常大 很多公司也都有在应用 他是 redux 和 redux-saga 的解决方案 可以简化操作 还内置了react-router 路由 和 fetch 网络请求 首先 它的学习并不困难 因为 Api本身其实比较少 他对于redux…

如何优化Apple搜索广告

Apple Search Ads是促进应用发展的工具&#xff0c;如果我们已经投放了广告&#xff0c;那么就要观察投放效果&#xff0c;及时识别广告薄弱的地方&#xff0c;可以给我们更多的机会去优化它并扩大其投放效果。 再开始投放广告之前&#xff0c;要确保我们的应用商店列表已经优…

vue_前后端项目分离操作-查询操作

前后端项目分离操作 使用搭建好的vue项目和ssm项目 功能需求分析 后端 查询 持久层 ​ 发送两条sql查询总条数和结果集(limit容易写死) ​ 使用分页插件pageHelper解决分页的功能 ​ 在pom.xml中添加依赖 <!--pagehelper--><dependency><groupId>com…

【C语言const关键字】

C语言const关键字 C语言之const关键字1、什么是const?2、const的用法2.1、const作常量的修饰符例程12.2、const修饰函数的参数例程2 3、const与指针变量的搭配3.1、指针与const的应用例程3.2、指针与const的应用延申二级指针 4、结束语 C语言之const关键字 前言&#xff1a; …

iOS iPadOS safari 独立Web应用屏幕旋转的时候 window.innerHeight 数值不对。

iOS iPadOS safari 独立Web应用屏幕旋转的时候 window.innerHeight 数值不对 一、问题描述 我有一个日记应用&#xff0c;是可以作为独立 Web 应用运行的那种&#xff0c;但在旋转屏幕的时候获取到的 window.innerHeight 和 window.innerWidth 就不对了&#xff0c;不是屏幕的…

无法安装此app,因为无法验证其完整性 ,解决方案

最近有很多兄弟萌跟我反应“无法安装此app,因为无法验证其完整性 ”&#xff0c;看来这个问题无法避免了&#xff0c;今天统一回复下&#xff0c;出现提示主要有以下几种可能 1.安装包不完整 首先申请我所有分享的破解软件全部都有自己校验过&#xff0c;一般不会存在问题出非你…

【视频观看记录】Bubbliiiing的Pytorch 搭建自己的Unet语义分割平台(Bubbliiiing 深度学习 教程)

来源 b站 地址 什么是语义分割 语义分割&#xff1a;对图像每个像素点进行分类 常见神经网络处理过程&#xff1a;Encoder提取特征&#xff0c;接着Docoder恢复成原图大小的图片 UNet整体结构 分为三个部分 主干特征提取部分&#xff1a; 卷积和最大池化的堆叠获得五个初…

win10安装pytorch GPU

我记得以前安装过深度学习库GPU版本&#xff0c; 需要安装cuda什么的&#xff0c;翻了下还真写过一篇win10安装tensorflow的文章&#xff0c;但是流程不止不详细&#xff0c;还不清晰。这次就再记录一遍 这次安装的是pytorch&#xff0c;这么多年似乎pytorch要逐渐统一深度学习…

JavaEE语法第二章之多线程(初阶二)

目录 一、线程常用方法 1.1启动一个线程-start() 1.2中断一个线程 1.2.1使用自定义的变量来作为标志位. 1.2.2使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位. 1.2.3观察标志位是否清除 1.3等待一个线程-join() 1.4获取当前…

Typora文本的使用

1. 如何创建目录&#xff1f; 输入几个#&#xff0c;再加空格&#xff0c;写入文字回车后就是几级标题&#xff1b; 2. 如何输入代码块&#xff1f; 英文状态下&#xff0c;输入三个反引号&#xff0c;然后回车即可&#xff1b; 3. 如何输入竖线和小圆点&#xff1f; 4. 如何…

SSH远程直连Docker容器

文章目录 1. 下载docker镜像2. 安装ssh服务3. 本地局域网测试4. 安装cpolar5. 配置公网访问地址6. SSH公网远程连接测试7.固定连接公网地址8. SSH固定地址连接测试8. SSH固定地址连接测试 转载自cpolar极点云文章&#xff1a;SSH远程直连Docker容器 在某些特殊需求下,我们想ssh…

使用显式特征的在线交互感知提升网络

目录 1介绍 2相关工作 2.1提升建模 2.2特征交互 3前提 4提出的方法 4.1架构 4.2训练 5试验评估 6结论和未来 英文题目&#xff1a;Explicit Feature Interaction-aware Uplift Network for Online Marketing 翻译&#xff1a;使用显式特征的在线交互感知提升网络 单…