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

目录

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.xdnf.cn/news/1557168.html

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

相关文章

java语言基础案例-cnblog

java语言基础案例 象棋口诀 输出 package nb;public class XiangQi {public static void main(String[] args) {char a 马;char b 象;char c 卒;System.out.println(a"走日"b"走田""小"c"一去不复还");} }输出汇款单 package nb…

30 树 · 二叉树

目录 一、树 &#xff08;一&#xff09;树的概念与结构 &#xff08;二&#xff09;树相关术语 &#xff08;三&#xff09;树的表示 &#xff08;四&#xff09;树形结构的实际应用场景 二、二叉树 &#xff08;一&#xff09;概念与结构 &#xff08;二&#xff09;…

【LeetCode】每日一题 2024_10_7 最低加油次数(堆、贪心)

前言 每天和你一起刷 LeetCode 每日一题~ 大家国庆节快乐呀~ LeetCode 启动&#xff01; 国庆最后一天&#xff0c;力扣还在加油站&#xff0c;怕不是国庆回家路上堵车了 题目&#xff1a;最低加油次数 代码与解题思路 func minRefuelStops(target int, startFuel int, st…

刷题 双指针 滑动窗口

面试经典 150 题 - 双指针 ⭐️125. 验证回文串 学会内部字母处理函数的使用 class Solution { public:bool isPalindrome(string s) {int left 0, right s.size() - 1;while (left < right) {// 处理左边字符if (!isalnum(s[left])) {left;continue;}// 处理右边字符if…

C(十五)函数综合(一)--- 开公司吗?

在这篇文章中&#xff0c;杰哥将带大家 “开公司”。 主干内容部分&#xff08;你将收获&#xff09;&#xff1a;&#x1f449; 为什么要有函数&#xff1f;函数有哪些&#xff1f;怎么自定义函数以及获得函数的使用权&#xff1f;怎么对函数进行传参&#xff1f;函数中变量的…

C语言 | Leetcode C语言题解之第462题最小操作次数使数组元素相等II

题目&#xff1a; 题解&#xff1a; static inline void swap(int *a, int *b) {int c *a;*a *b;*b c; }static inline int partition(int *nums, int left, int right) {int x nums[right], i left - 1;for (int j left; j < right; j) {if (nums[j] < x) {swap(…

Linux 外设驱动 应用 1 IO口输出

从这里开始外设驱动介绍&#xff0c;这里使用的IMX8的芯片作为驱动介绍 开发流程&#xff1a; 修改设备树&#xff0c;配置 GPIO1_IO07 为 GPIO 输出。使用 sysfs 接口或编写驱动程序控制 GPIO 引脚。编译并测试。 这里假设设备树&#xff0c;已经配置好了。不在论述这个问题…

【英语】5. 考研英语语法体系

文章目录 前言句字的成分一、常规句型简单句&#xff08;5 种&#xff09;1. 定义&#xff1a;句子中只包含 *一套主谓结构* 的句子。&#xff08;一个句子只能有一个谓语动词&#xff09;2. 分类 并列句&#xff08;由关联词组成&#xff09;&#xff08;3 种&#xff09;基本…

二分图算法总结 C++实现

总体概念 染色法 基本思路步骤 将所有的边及其相接的边用邻接表存储起来&#xff1b;遍历所有的点&#xff0c;找到未上色的点&#xff1b;用BFS将该点及其相接的点迭代上色&#xff1b;在上述染色步骤中&#xff0c;如果相邻点的颜色相同则无法形成二分图&#xff1b; 题目…

继电保护之电压重动、电压并列和电压切换

实践&#xff1a;以某开关室10kV母联隔离柜为例&#xff1a; ZYQ-824为PT并列装置&#xff0c;装置内包含一系列继电器&#xff0c;用于PT重动及并列。按照装置编号原则&#xff0c;交流电压切换箱一般命名为7n。 ​下图为装置内继电器线圈部分接线&#xff1a; 下图为装置内…

销售秘籍:故事+观点+结论

在销售的浩瀚宇宙中&#xff0c;隐藏着一个不朽的秘诀——利用人类共有的“错失恐惧”&#xff0c;激发客户内心的渴望与行动。正如村上春树所言&#xff0c;每个故事都深深植根于灵魂&#xff0c;而大仲马则揭示&#xff0c;灵魂之眼所见&#xff0c;比肉眼更为长久铭记。 错…

如何将数据从 AWS S3 导入到 Elastic Cloud - 第 1 部分:Elastic Serverless Forwarder

作者&#xff1a;来自 Elastic Hemendra Singh Lodhi 这是多部分博客系列的第一部分&#xff0c;探讨了将数据从 AWS S3 导入 Elastic Cloud 的不同选项。 Elasticsearch 提供了多种从 AWS S3 存储桶导入数据的选项&#xff0c;允许客户根据其特定需求和架构策略选择最合适的方…

Mysql锁机制解读(敲详细)

目录 锁的概念 全局锁 表级锁 表锁 元数据锁 意向锁 锁的概念 全局锁 表级锁 表锁 元数据锁 主要是对未提交事务&#xff0c;修改表结构造成表结构混乱&#xff0c;进行控制。 在不涉及表结构变化的情况下,元素锁可以忽略。 意向锁 避免有行级锁影响加表级锁&#xff0…

YoloV9改进策略:BackBone改进|CAFormer在YoloV9中的创新应用,显著提升目标检测性能

摘要 在目标检测领域,模型性能的提升一直是研究者和开发者们关注的重点。近期,我们尝试将CAFormer模块引入YoloV9模型中,以替换其原有的主干网络,这一创新性的改进带来了显著的性能提升。 CAFormer,作为MetaFormer框架下的一个变体,结合了深度可分离卷积和普通自注意力…

​.NET一款反序列化执行命令的白名单工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

Unity_Obfuscator Pro代码混淆工具_学习日志

Unity_Obfuscator Pro代码混淆工具_学习日志 切勿将密码或 API 密钥存储在您附带的应用程序内。 混淆后的热更新暂时没有想到怎么办 Obfuscator 文档 https://docs.guardingpearsoftware.com/manual/Obfuscator/Description.html商店链接Obfuscator Pro&#xff08;大约$70&a…

sqli-labs靶场第三关less-3

sqli-labs靶场第三关less-3 1、确定注入点 http://192.168.128.3/sq/Less-3/?id1 http://192.168.128.3/sq/Less-3/?id2 有不同回显&#xff0c;判断可能存在注入&#xff0c; 2、判断注入类型 输入 http://192.168.128.3/sq/Less-3/?id1 and 11 http://192.168.128.3/sq/L…

Nginx07-静态资源访问

零、文章目录 Nginx07-静态资源访问 1、Nginx解决跨域问题 &#xff08;1&#xff09;同源策略 同源策略&#xff08;Same-Origin Policy&#xff09;是一个关键的网络安全概念&#xff0c;由Netscape公司在1995年引入&#xff0c;现在被所有现代浏览器所采用。它限制了从一…

每日一题|871. 最低加油次数|动态规划、内层逆序遍历

题目分析&#xff1a;找到第一个能够实现当前油量能够行驶的最大距离大于等于目标距离&#xff0c;且加油次数最小的加油站和加油次数。 每次加油之后能够行驶的最大距离都是由上一次加油的距离决定的&#xff0c;因此使用dp。 1、dp数组定义 dp[i]是一个一维数组&#xff0…

毕业设计项目 深度学习安全帽佩戴检测(源码+论文)

文章目录 0 前言1 项目运行效果2 设计概要3 最后 0 前言 &#x1f525;这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师…