动态规划算法专题(四):子串、子数组系列

目录

1、最大子数组和

1.1 算法原理

1.2 算法代码

2、环形子数组的最大和

2.1 算法原理

2.2 算法代码

3、乘积最大子数组

3.1 算法原理

3.2 算法代码

4、乘积为正数的最长子数组长度

4.1 算法原理

4.2 算法代码

5、等差数列划分

5.1 算法原理 

5.2 算法代码

6、最长湍流子数组

6.1 算法原理 

6.2 算法代码

7、单词拆分

7.1 算法原理

7.2 算法代码

8、环绕字符串中唯一的子字符串

8.1 算法原理 

 8.2 算法代码


1、最大子数组和

. - 力扣(LeetCode)

1.1 算法原理

  • 状态表示dp[i]:

以i位置为结尾,最长子序列之和

  • 状态转移方程:

dp[i]=max(dp[i-1]+nums[i-1],nums[i-1]);

  • 初始化:

int[] dp = new int[n+1];
dp[0]=0;

  • 填表顺序:

从左往右

  • 返回值:

dp中最大序列和

1.2 算法代码

class Solution {public int maxSubArray(int[] nums) {int n = nums.length;int[] dp = new int[n + 1];// 初始化dp[0] = 0;int ret = Integer.MIN_VALUE;// 填表for(int i = 1; i <= n; i++) {dp[i] = Math.max(dp[i - 1] + nums[i - 1], nums[i - 1]);ret = Math.max(ret, dp[i]);}return ret;}
}

2、环形子数组的最大和

. - 力扣(LeetCode)

2.1 算法原理

核心:将带环问题转化为普通不带环问题

  • 状态表示:

f[i]:以i位置为结尾,最大子序列之和
g[i]:以i位置为结尾,最小子序列之和

  • 状态转移方程:

f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);

g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);

  • 初始化:

下标映射:dp[i]-->nums[i-1]
虚拟节点:要保证后续填表的正确性:f[0]=g[0]=0;

  • 填表顺序:

从左往右,两个表一起填。

  • 返回值(特殊情况:当表中的数都是负数时(都在最小子序列中),此时sum-gMin=0):

找到f表中的最大值 fMax;
找到g表中的最小值 gMin,进而得到最大和序列:sum-gMin;
return sum == gMin ? fMax : max(fMax, sum-gMin);

2.2 算法代码

class Solution {public int maxSubarraySumCircular(int[] nums) {int n = nums.length;int sum = 0;for(int x : nums) sum += x;int[] f = new int[n + 1];//内部最大子数组int[] g = new int[n + 1];//内部最小子数组f[0] = g[0] = 0;int fMax = Integer.MIN_VALUE;int gMin = Integer.MAX_VALUE;for(int i = 1; i <= n; i++) {f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);fMax = Math.max(fMax, f[i]);gMin = Math.min(gMin, g[i]);}// 注意数组中全是负数的情况return sum == gMin ? fMax : Math.max(fMax, sum - gMin);}
}

3、乘积最大子数组

. - 力扣(LeetCode)

3.1 算法原理

  • 状态表示:

f[i]:以i位置为结尾,最大乘积
g[i]:以i位置为结尾,最小乘积

  • 状态转移方程:

f[i]=max(nums[i-1], f[i-1]*nums[i-1], g[i-1]*nums[i-1]);
g[i]=min(nums[i-1], f[i-1]*nums[i-1], g[i-1]*nums[i-1]);

  • 初始化:

f[1]=g[1]=1;

  • 建表顺序:

从左往右,两个表一起填

  • 返回值:

f表中的最大值

3.2 算法代码

class Solution2 {public int maxProduct(int[] nums) {int n = nums.length;int[] f = new int[n + 1];// 以i位置为结尾,最大乘积int[] g = new int[n + 1];// 以i位置为结尾,最小乘积int ret = Integer.MIN_VALUE;// 初始化f[0] = g[0] = 1;// 建表for(int i = 1; i <= n; i++) {f[i] = Math.max(nums[i - 1], Math.max(nums[i - 1] * f[i - 1], nums[i - 1] * g[i - 1]));g[i] = Math.min(nums[i - 1], Math.min(nums[i - 1] * f[i - 1], nums[i - 1] * g[i - 1]));ret = Math.max(ret, f[i]);}return ret;}
}

4、乘积为正数的最长子数组长度

. - 力扣(LeetCode)

4.1 算法原理

  • 状态表示:

f[i]:以i位置为结尾,乘积为 正数 的,最长子序列的长度。

g[i]:以i位置为结尾,乘积为 负数 的,最长子序列的长度。

  • 状态转移方程:

①:nums[i] > 0

f[i] = f[i-1]+1; g[i] = (g[i-1]==0 ? 0 : g[i-1]+1);

②:nums[i] < 0

f[i] = (g[i-1]==0 ? 0 : g[i-1]+1); g[i] = f[i-1]+1;

  • 初始化:

①:f[0]=g[0]=0; // 虚拟节点的值,不影响后续填表的正确性

②:下标映射关系

  • 返回值:

f表中的最大值

4.2 算法代码

class Solution {public int getMaxLen(int[] nums) {int n = nums.length;int[] f = new int[n + 1];// 乘积为正数的最长序列的长度int[] g = new int[n + 1];// 乘积为负数的最长序列的长度int ret = 0;// 填表for(int i = 1; i <= n; i++) {if(nums[i - 1] > 0) {f[i] = f[i - 1] + 1;g[i] = (g[i - 1] == 0 ? 0 : g[i - 1] + 1);}if(nums[i - 1] < 0) {f[i] = (g[i - 1] == 0 ? 0 : g[i - 1] + 1);g[i] = f[i - 1] + 1;}ret = Math.max(ret, f[i]);}return ret;}
}

5、等差数列划分

. - 力扣(LeetCode)

5.1 算法原理 

  • 状态表示dp[i]:

以i位置为结尾的所有子序列中,等差序列(长度>=3)的数量

  • 状态转移方程:

①:nums[i]-nums[i-1] == nums[i-1]-nums[i-2] => dp[i]=dp[i-1]+1;

②:nums[i]-nums[i-1] != nums[i-1]-nums[i-2] => dp[i]=0;

  • 初始化:

dp[0]=dp[1]=0;(子序列长度不足3)

  • 返回值:

dp表之和

5.2 算法代码

class Solution {public int numberOfArithmeticSlices(int[] nums) {int n = nums.length;int[] dp = new int[n];int ret = 0;// 填表for(int i = 2; i < n; i++) {if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) {dp[i] = dp[i - 1] + 1;ret += dp[i];}}return ret;}
}

6、最长湍流子数组

. - 力扣(LeetCode)

6.1 算法原理 

  • 状态表示:

f[i]:以i位置为结尾,最后呈现"上升趋势"的最长子序列的长度
g[i]:以i位置为结尾,最后呈现"下降趋势"的最长子序列的长度

  • 状态转移方程:

if(nums[i] > nums[i-1]) f[i]=g[i-1]+1;
if((nums[i] < nums[i-1]) g[i]=f[i-1]+1;

  • 初始化:

f表,g表中所有元素的值初始化为 1 
(最差情况序列长度为1)

  • 建表顺序:

从左往右

  • 返回值:

f 和 g 表中的最大值

6.2 算法代码

class Solution {public int maxTurbulenceSize(int[] arr) {int n = arr.length;int[] f = new int[n];// f[i]: 最后呈现"上升"趋势的最长湍流子数组的长度int[] g = new int[n];// g[i]: 最后呈现"下降"趋势的最长湍流子数组的长度// 初始化Arrays.fill(f, 1);Arrays.fill(g, 1);int ret = 1;// 建表 + 处理返回值for(int i = 1; i < n; i++) {if(arr[i] > arr[i - 1]) f[i] = g[i - 1] + 1;if(arr[i] < arr[i - 1]) g[i] = f[i - 1] + 1;ret = Math.max(ret, Math.max(f[i], g[i]));}return ret;}
}

7、单词拆分

. - 力扣(LeetCode)

7.1 算法原理

  • 状态表示dp[i]:

[0, i]区间的字符串,能否被字典中的单词拼接而成

  • 状态转移方程:

根据最后一个单词的位置,划分问题。
设j为最后一个单词的起始位置(0<=j<=i),
判断条件①:[j, i]区间的字符串是否存在于字典中
判断条件②:[0, j-1]区间的字符串是否可被拼接而成

  • 初始化:

 dp[0]=true;(里面的值要保证后续的填表是正确的)
 优化:s = s+ " ";(下标映射的处理)

  • 填表顺序:

从左往右

  • 返回值:

dp[n];

7.2 算法代码

class Solution {public boolean wordBreak(String s, List<String> wordDict) {// 优化一:哈希Set<String> hash = new HashSet<>(wordDict);int n = s.length();boolean[] dp = new boolean[n + 1];s = " " + s;// 初始化dp[0] = true;for(int i = 1; i <= n; i++) {for(int j = i; j >= 1; j--) {if(hash.contains(s.substring(j, i + 1)) && dp[j - 1]) {dp[i] = true;// 优化二break;}}}return dp[n];}
}

8、环绕字符串中唯一的子字符串

. - 力扣(LeetCode)

8.1 算法原理 

  • 状态表示:

以i位置字符为结尾的所有不同的子串,有多少个在base中出现过

  • 状态转移方程:

if(s[i-1]+1 == s[i] || (s[i-1] == 'z' && s[i] == 'a')) --> dp[i] += dp[i-1];

  • 初始化:

Arrays.fill(dp, 1);// 最差情况下,字符本身构成base中的子串

  • 填表顺序:

从左往右

  • 返回值:

给重复的子串去重:
相同字符结尾的dp值,取最大的即可(虽然是相同字符结尾的子串,但dp值更大的这种情况,
包含了dp值小的情况):
1. int[26] hash // 存相应字符结尾的最大的dp值
2. 返回hash数组的和

 8.2 算法代码

class Solution {public int findSubstringInWraproundString(String ss) {char[] s = ss.toCharArray();int n = s.length;// 状态表示:以i位置字符为结尾的所有不同的子串,有多少个在base中出现过int[] dp = new int[n];int[] hash = new int[26];Arrays.fill(dp, 1);// 填表for(int i = 1; i < n; i++) {if(s[i - 1] + 1 == s[i] || (s[i - 1] == 'z' && s[i] == 'a')) {dp[i] += dp[i - 1];}}// 去重for(int i = 0; i < n; i++) {int index = s[i] - 'a';hash[index] = Math.max(hash[index], dp[i]);}// 返回结果int ret = 0;for(int x : hash) ret += x;return ret; }
}

END

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

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

相关文章

COSPLAY大赛静态HTML网页模板源码

源码名称&#xff1a;COSPLAY大赛静态HTML网页模板 源码介绍&#xff1a;一款cosplay大赛HTML网页模板源码&#xff0c;过往参赛选手会自动从腾讯大赛获取&#xff0c;可用于cosplay大赛&#xff0c;漫展等。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.5188…

vue2路由和vue3路由区别及原理

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中&#xff0c;通过 Vue.use() 注册路由插件&#xff0c;并通过 new VueRouter() 来创建路由实例。 import Vue from vue;import VueRouter from vue-router;import Home from /components/Home.vue;​Vue.us…

如何减少网络安全事件的损失

为了减轻网络安全事件的损失&#xff0c;可以采取以下多项措施&#xff1a; 一、数据备份与恢复 定期备份数据&#xff1a;包括文件、数据库和系统映像等&#xff0c;确保在发生安全事件时&#xff0c;能够快速恢复数据和系统&#xff0c;从而减少损失。制定恢复计划&#xf…

k8s的控制节点不能访问node节点容器的ip地址

master控制node服务器添加容器后,访问不了该node服务器容器的ip,只能在node服务器访问 排查后发现是k8s的master服务器和node节点的网址网段和k8s初始化时提示的ip网段不一致 我之前是192.168.137.50, 实际上master主机期望的是192.168.1.50 解决方案: 1.删除服务器后重建ma…

使用iTextPDF库时,设置文字为中文格式

在使用iTextPDF库时&#xff0c;设置文字为中文格式主要涉及选择合适的中文字体&#xff0c;并确保该字体能够正确渲染中文字符。由于iTextPDF的内置字体通常不支持中文&#xff0c;因此你需要加载一个支持中文的字体文件&#xff08;如TrueType字体&#xff0c;.ttf文件&#…

SpringCloud入门(十二)全局过滤器和跨域

一、全局过滤器 全局过滤器的作用也是处理一切进入网关的请求和微服务响应&#xff0c;与GatewayFilter的作用一样。 区别在于GatewayFilter通过配置定义&#xff0c;处理逻辑是固定的&#xff0c;如果我们希望拦截请求&#xff0c;做自己的业务逻辑则没办法实现。而GlobalFilt…

【api连接ChatGPT的最简单方式】

通过api连接ChatGPT的最简单方式 建立client 其中base_url为代理&#xff0c;若连接官网可省略&#xff1b;配置环境变量 from openai import OpenAI client OpenAI(base_url"https://api.chatanywhere.tech/v1" )或给出api和base_url client OpenAI(api_key&…

英文论文安全的免费查重网站

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 英文论文安全的免费查重网站 对于英文论文查重&#xff0c;以下是一些相对安全、且免费使用的查重网站推荐。不过请注意&#xff0c;免费工具通常只能提供基础查重功能&#xff0c;对于学术论文的全面查重&…

【AI大模型-文心-思维树解读-仓颉精通之路-11】

提问&#xff1a;你还记得我们设置过一个暗号吗&#xff1f;仓颉精通之路 回答&#xff1a;如下 版本&#xff1a;文心大模型3.5 是的&#xff0c;我记得我们之前设置过的“暗号”是“仓颉精通之路”。这个“暗号”可以被视为一个象征性的口号或指引&#xff0c;代表着我们在追…

VLAN 和 ARP 表

VLAN 和 ARP 表之间有着密切的关系&#xff0c;因为 ARP (地址解析协议) 用于在同一个二层广播域内将 IP 地址解析为 MAC 地址。VLAN 将一个物理局域网分割成多个逻辑局域网&#xff0c;每个 VLAN 都是一个独立的广播域。因此&#xff0c;ARP 表的作用范围被限制在每个 VLAN 内…

Redis:string类型

Redis&#xff1a;string类型 string命令设置与读取SETGETMSETMGET 数字操作INCRINCRBYDECRDECRBYINCRBYFLOAT 字符串操作APPENDSTRLENGETRANGESETRANGE 内部编码intembstrraw 在Redis中&#xff0c;字符串string存储的是二进制&#xff0c;以byte为单位&#xff0c;输入的二进…

Pikachu-Unsafe FileUpload-客户端check

上传图片&#xff0c;点击查看页面的源码&#xff0c; 可以看到页面的文件名校验是放在前端的&#xff1b;而且也没有发起网络请求&#xff1b; 所以&#xff0c;可以通过直接修改前端代码&#xff0c;删除 checkFileExt(this.value) 这部分&#xff1b; 又或者先把文件名改成…

java代理模式(动态代理、静态代理、需要实现类的JDK代理、不需要实现类的JDK动态代理、CGLIB代理)

静态代理简单使用 静态代理是代理模式的一种实现方式&#xff0c;它在编译时就已经确定了被代理对象和代理对象的关系。在静态代理中&#xff0c;需要手动创建一个代理类&#xff0c;该代理类与被代理对象实现相同的接口或继承相同的父类&#xff0c;并在代理类的方法中调用被…

C++基类构造器的自动调用

C基类构造器的自动调用 虽然基类的构造器和解构器不会被派生类继承&#xff0c;但它们会被派生类的构造器和解构器自动调用&#xff0c;今天我们用代码实证一下。 验证代码 源代码&#xff0c;仔细看注释内容&#xff1a; D:\YcjWork\CppTour>vim c2004.cpp #include &l…

《RabbitMQ篇》基本概念介绍

MQ功能 解耦 MQ允许不同系统或组件之间松散耦合。发送者和接收者不需要直接连接&#xff0c;从而提高了系统的灵活性和可维护性。异步处理 使用MQ可以实现异步消息传递&#xff0c;发送者可以将消息放入队列后立即返回&#xff0c;不必等待接收者处理。这提高了系统的响应速度…

RabbitMQ入门3—virtual host参数详解

在 RabbitMQ 中&#xff0c;创建 Virtual Host 时会涉及到一些参数配置&#xff0c;比如 tags 和 Default Queue Type。下面是对这两个参数的详细解释&#xff1a; 1. Tags Tags 是 Virtual Host 的标记&#xff0c;用来为 Virtual Host 添加元数据&#xff0c;帮助你管理和组…

牛客周赛 Round 62(期望、DFS、主席树、DP、逆推DP)

文章目录 牛客周赛 Round 62&#xff08;期望、DFS、主席树、DP、逆推DP&#xff09;A. 小红的字符移动B. 小红的数轴移动C. 小红的圆移动D. 小红的树上移动 (期望、DFS)E. F. 小红的中位数查询&#xff08;主席树&#xff09;G. 小红的数轴移动&#xff08;二&#xff09;&…

Windows下Jenkins控制台中文乱码

问题描述 问题情况如下图&#xff1a; 环境信息 Windows 11 家庭中文版java 21.0.4 2024-07-16 LTSJenkins 2.452.3 解决方法 增加系统JAVA_TOOL_OPTIONS&#xff0c;并设置值为-Dfile.encodingGBK。 打开设置方法&#xff1a;桌面上右键点击“此电脑”图标&#xff0c;选…

mysql设置表的某一个字段每天定时清零

推荐学习文档 golang应用级os框架&#xff0c;欢迎stargolang应用级os框架使用案例&#xff0c;欢迎star案例&#xff1a;基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识&#xff0c;这里有免费的golang学习笔…

算法笔记(十三)——BFS 解决最短路问题

文章目录 迷宫中离入口最近的出口最小基因变化单词接龙为高尔夫比赛砍树 BFS 解决最短路问题 BFS(广度优先搜索) 是解决最短路径问题的一种常见算法。在这种情况下&#xff0c;我们通常使用BFS来查找从一个起始点到目标点的最短路径。 迷宫中离入口最近的出口 题目&#xff1a;…