目录

一、最大子数组和(LeetCode 53)

二、环形子数组的最大和(LeetCode 918)

三、乘积最大子数组(LeetCode 152)

四、乘积为正数的最长子数组长度(LeetCode 1567)

五、等差数列划分(LeetCode 413)

六、最长湍流子数组(LeetCode 978)

七、单词拆分(LeetCode 139)

 八、环绕字符串中唯一的子字符串(LeetCode 467)

总结


动态规划(Dynamic Programming,简称 DP)是解决多阶段决策问题的有效方法,尤其在处理子数组、子串相关问题时,能通过状态定义和转移,高效找到最优解。下面结合几道经典题目,分享动态规划在这类问题中的分析方法与思路。
 

一、最大子数组和(LeetCode 53)

 
问题描述
 
给定整数数组  nums ,找出和最大的连续子数组,返回其最大和。
 
分析思路
 
- 状态定义: dp[i]  表示以  nums[i]  结尾的连续子数组的最大和。
- 状态转移:对于  nums[i] ,有两种选择,要么加入前面的子数组( dp[i - 1] + nums[i] ),要么自己作为新子数组的起点( nums[i] ),取两者最大值,即  dp[i] = max(nums[i], dp[i - 1] + nums[i]) 。
- 初始条件: dp[0] = nums[0] ,因为第一个元素自己就是一个子数组。
- 结果提取:遍历  dp  数组,找到最大值。
 
代码实现
 

class Solution {
public:int maxSubArray(vector<int>& nums) {int n = nums.size();vector<int> dp(n);dp[0] = nums[0];for (int i = 1; i < n; i++) {dp[i] = max(nums[i], dp[i - 1] + nums[i]);}int maxSum = INT_MIN;for (int num : dp) {maxSum = max(maxSum, num);}return maxSum;}
};

二、环形子数组的最大和(LeetCode 918)


 
问题描述
 
给定环形整数数组  nums ,返回非空子数组的最大可能和(环形意味着数组末端与开头相连)。
 
分析思路
 
环形子数组的最大和有两种情况:
 
- 情况一:最大和子数组在数组内部(非环形),可通过常规最大子数组和方法计算。
- 情况二:最大和子数组跨越数组首尾(环形),此时最大和等于数组总和减去内部最小子数组和。
需要注意,若数组所有元素都是负数,此时总和减去最小子数组和(也是负数)会得到错误结果,需单独判断。
 
代码实现

class Solution {
public:int maxSubarraySumCircular(vector<int>& nums) {int n = nums.size();int sum = 0;vector<int> f(n + 1);auto g = f;f[0] = g[0] = 0;for (int i = 1; i <= n; i++) {sum += nums[i - 1];f[i] = max(nums[i - 1], f[i - 1] + nums[i - 1]);g[i] = min(nums[i - 1], g[i - 1] + nums[i - 1]);}int fmax = INT_MIN, gmin = INT_MAX;for (int i = 1; i <= n; i++) {fmax = max(fmax, f[i]);gmin = min(gmin, g[i]);}if (gmin == sum) return fmax;return max(fmax, sum - gmin);}
};

三、乘积最大子数组(LeetCode 152)


 
问题描述
 
给定整数数组  nums ,找出乘积最大的非空连续子数组,返回该乘积。
 
分析思路
 
由于乘法的特殊性,负数相乘可能得到正数,所以需要同时记录以  nums[i]  结尾的子数组的最大乘积和最小乘积。
 
- 状态定义: f[i]  表示以  nums[i]  结尾的子数组的最大乘积, g[i]  表示以  nums[i]  结尾的子数组的最小乘积。
- 状态转移:根据  nums[i]  的正负,选择与  f[i - 1]  或  g[i - 1]  相乘,即:
- 若  nums[i] > 0 , f[i] = max(nums[i], f[i - 1] * nums[i]) , g[i] = min(nums[i], g[i - 1] * nums[i]) ;
- 若  nums[i] < 0 , f[i] = max(nums[i], g[i - 1] * nums[i]) , g[i] = min(nums[i], f[i - 1] * nums[i]) ;
- 若  nums[i] == 0 , f[i] = g[i] = 0 。
- 初始条件: f[0] = g[0] = nums[0] 。
- 结果提取:遍历  f  数组,找到最大值。
 
代码实现

class Solution {
public:int maxProduct(vector<int>& nums) {int n = nums.size();vector<int> f(n);f[0] = nums[0];vector<int> g(n);g[0] = nums[0];for (int i = 1; i < n; i++) {if (nums[i] > 0) {f[i] = max(nums[i], f[i - 1] * nums[i]);g[i] = min(nums[i], g[i - 1] * nums[i]);} else if (nums[i] < 0) {f[i] = max(nums[i], g[i - 1] * nums[i]);g[i] = min(nums[i], f[i - 1] * nums[i]);} else {f[i] = g[i] = 0;}}int maxProd = INT_MIN;for (int num : f) {maxProd = max(maxProd, num);}return maxProd;}
};

四、乘积为正数的最长子数组长度(LeetCode 1567)


 
问题描述
 
给定整数数组  nums ,求出乘积为正数的最长子数组的长度。
 
分析思路
 
- 状态定义: f[i]  表示以  nums[i - 1]  结尾的乘积为正数的最长子数组长度, g[i]  表示以  nums[i - 1]  结尾的乘积为负数的最长子数组长度。
- 状态转移:根据  nums[i - 1]  的正负进行转移:
- 若  nums[i - 1] > 0 , f[i] = f[i - 1] + 1 , g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1 ;
- 若  nums[i - 1] < 0 , f[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1 , g[i] = f[i - 1] + 1 ;
- 若  nums[i - 1] == 0 , f[i] = g[i] = 0 。
- 初始条件: f[0] = g[0] = 0 。
- 结果提取:遍历过程中记录  f[i]  的最大值。
 
代码实现
 

class Solution {
public:int getMaxLen(vector<int>& nums) {int n = nums.size();vector<int> f(n + 1);vector<int> g(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;} else if (nums[i - 1] < 0) {f[i] = (g[i - 1] == 0) ? 0 : g[i - 1] + 1;g[i] = f[i - 1] + 1;} else {f[i] = 0;g[i] = 0;}ret = max(ret, f[i]);}return ret;}
};


五、等差数列划分(LeetCode 413)


 
问题描述
 
如果一个数列至少有三个元素,且任意两个相邻元素之差相同,则称该数列为等差数列。给定整数数组  nums ,返回数组中所有为等差数组的子数组个数。
 
分析思路
 
- 状态定义: dp[i]  表示以  nums[i]  结尾的等差数列的个数。
- 状态转移:若  nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2] ,说明以  nums[i]  结尾能形成新的等差数列, dp[i] = dp[i - 1] + 1 ;否则  dp[i] = 0 。
- 初始条件: dp[0] = dp[1] = 0 ,因为前两个元素无法形成至少三个元素的等差数列。
- 结果提取:将所有  dp[i]  相加,得到总的等差数列子数组个数。
 
代码实现

  
class Solution {
public:int numberOfArithmeticSlices(vector<int>& nums) {int n = nums.size();if (n < 3) return 0;vector<int> dp(n);dp[0] = dp[1] = 0;int sum = 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;} else {dp[i] = 0;}sum += dp[i];}return sum;}
};

六、最长湍流子数组(LeetCode 978)


 
问题描述
 
给定整数数组  arr ,返回其最大湍流子数组的长度。湍流子数组是指相邻元素对之间比较符号翻转的子数组。
 
分析思路
 
- 状态定义: f[i]  表示以  arr[i]  结尾,且最后是上升( arr[i] > arr[i - 1] )的湍流子数组长度; g[i]  表示以  arr[i]  结尾,且最后是下降( arr[i] < arr[i - 1] )的湍流子数组长度。
- 状态转移:
- 若  arr[i] > arr[i - 1] , f[i] = g[i - 1] + 1 , g[i] = 1 ;
- 若  arr[i] < arr[i - 1] , g[i] = f[i - 1] + 1 , f[i] = 1 ;
- 若  arr[i] == arr[i - 1] , f[i] = g[i] = 1 。
- 初始条件: f[0] = g[0] = 1 ,单个元素自身是长度为 1 的湍流子数组。
- 结果提取:遍历过程中记录  f[i]  和  g[i]  中的最大值。
 
代码实现

  
class Solution {
public:int maxTurbulenceSize(vector<int>& arr) {int n = arr.size();vector<int> f(n, 1);vector<int> g(n, 1);int ret = 1;for (int i = 1; i < n; i++) {if (arr[i] > arr[i - 1]) {f[i] = g[i - 1] + 1;g[i] = 1;} else if (arr[i] < arr[i - 1]) {g[i] = f[i - 1] + 1;f[i] = 1;} else {f[i] = g[i] = 1;}ret = max(ret, max(f[i], g[i]));}return ret;}
};

七、单词拆分(LeetCode 139)


 
问题描述
 
给定字符串  s  和字符串列表  wordDict  作为字典,判断是否可以利用字典中出现的一个或多个单词拼接出  s 。
 
分析思路
 
- 状态定义: dp[i]  表示字符串  s  的前  i  个字符是否能被字典拼接而成。
- 状态转移:对于每个  i ,枚举分割点  j (从  i  到  1 ),若  dp[j - 1]  为  true (前  j - 1  个字符能被拼接)且  s  的  j  到  i  子串在字典中,则  dp[i] = true 。
- 初始条件: dp[0] = true ,空字符串能被拼接。
- 结果提取: dp[s.size()]  即为最终结果,表示整个字符串是否能被拼接。
 
代码实现

class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {unordered_set<string> hash;for (auto &e : wordDict) hash.insert(e);int n = s.size();vector<bool> dp(n + 1);dp[0] = true;s = " " + s;for (int i = 1; i <= n; i++) {for (int j = i; j >= 1; j--) {if (dp[j - 1] && hash.count(s.substr(j, i - j + 1))) {dp[i] = true;break;}}}return dp[n];}
};


 八、环绕字符串中唯一的子字符串(LeetCode 467)


 
问题描述
 
定义字符串  base  为  "abcdefghijklmnopqrstuvwxyz"  无限环绕的字符串,给定字符串  s ,统计并返回  s  中有多少不同非空子串也在  base  中出现。
 
分析思路
 
- 状态定义: dp[i]  表示以  s[i]  结尾的符合  base  规律的子串长度。
- 状态转移:若  s[i - 1]  到  s[i]  符合  base  的连续规律(如  s[i - 1]  是  'z'  且  s[i]  是  'a' ,或  s[i] = s[i - 1] + 1 ),则  dp[i] = dp[i - 1] + 1 ;否则  dp[i] = 1 。
- 去重处理:用长度为 26 的数组  hash  记录每个字符结尾的最长符合规律子串长度,最后将所有  hash  元素相加,得到不同子串的个数(因为最长长度包含了所有更短的以该字符结尾的符合规律子串)。
 
代码实现
   

class Solution {
public:int findSubstringInWraproundString(string s) {int n = s.size();vector<int> dp(n, 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];}}int hash[26] = {0};for (int i = 0; i < n; i++) {hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);}int sum = 0;for (auto e : hash) {sum += e;}return sum;}
};

总结

动态规划在子数组、子串问题中,核心是定义合适的状态,并找到清晰的状态转移方程。不同问题的状态定义角度不同,有的关注以当前元素结尾的子数组的某种属性(和、乘积、长度等),有的关注全局的可能性(如是否能拼接)。通过多练习这类题目,能逐渐掌握动态规划的思维方式,更高效地解决复杂问题。

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

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

相关文章

微信小程序开发笔记(01_小程序基础与配置文件)

ZZHow(ZZHow1024) 参考课程: 【尚硅谷微信小程序开发教程】 [https://www.bilibili.com/video/BV1LF4m1E7kB] 009_文件和目录结构介绍新建页面与调试基础库 一个完整的小程序项目分为两个部分&#xff1a;主体文件、页面文件 主体文件又称全局文件&#xff0c;能够作用于整…

NLP Subword 之 BPE(Byte Pair Encoding) 算法原理

本文将介绍以下内容&#xff1a; 1. BPE 算法核心原理2. BPE 算法流程3. BPE 算法源码实现DemoBPE最早是一种数据压缩算法&#xff0c;由Sennrich等人于2015年引入到NLP领域并很快得到推广。该算法简单有效&#xff0c;因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算…

CSS 伪类选择器

伪类选择器&#xff08;pseudo-class selector&#xff09;是一种用于选择HTML元素特定状态或特征的关键字&#xff0c;它允许开发者基于文档树之外的信息&#xff08;如用户交互、元素位置或状态变化&#xff09;来选择元素并应用样式。伪类选择器以冒号(:)开头&#xff0c;附…

Electron 新特性:2025 版本更新解读

引言&#xff1a;Electron 新特性在 2025 版本更新中的解读核心价值与必要性 在 Electron 框架的持续演进中&#xff0c;新特性的引入是推动桌面开发创新的核心动力&#xff0c;特别是 2025 年的版本更新&#xff0c;更是 Electron 项目从成熟生态到前沿技术的跃进之钥。它不仅…

MyBatis从入门到面试:掌握持久层框架的精髓

MyBatis从入门到面试&#xff1a;掌握持久层框架的精髓 前言 在Java企业级应用开发中&#xff0c;持久层框架的选择至关重要。MyBatis作为一款优秀的半自动化ORM框架&#xff0c;以其灵活的SQL定制能力和良好的性能表现&#xff0c;成为了众多开发者的首选。本文将带你从MyBa…

5.Three.js 学习(基础+实践)

Three.js 是 “WebGL 的封装库”&#xff0c;帮你屏蔽了底层的着色器 / 缓冲区细节&#xff0c;专注于 “3D 场景搭建”&#xff0c;开发效率高&#xff0c;是通用 3D 开发的首选。他的核心是 “场景 - 相机 - 渲染器” 的联动逻辑&#xff0c;先掌握基础组件&#xff0c;再学进…

消火栓设备工程量计算 -【图形识别】秒计量

消火栓设备工程量计算 -【图形识别】秒计量 消防系统的消火栓设备水枪、水带和消火栓组成&#xff0c;根据清单定额规则计算消火栓设备工程量。通过CAD快速看图的图形识别框选图纸就能自动数出消火栓数量&#xff0c;省时又准确&#xff0c;是工程人做消防算量的好帮手。 一、…

Docker 与 VSCode 远程容器连接问题深度排查与解决指南

Docker 与 VSCode 远程容器连接问题深度排查与解决指南 引言 Visual Studio Code 的 Remote - Containers 扩展极大地提升了开发体验&#xff0c;它将开发环境容器化&#xff0c;保证了环境的一致性&#xff0c;并允许开发者像在本地一样在容器内进行编码、调试和运行。然而&…

爱图表:镝数科技推出的智能数据可视化平台

本文转载自&#xff1a;https://www.hello123.com/aitubiao ** 一、✨ AI 图表&#xff1a;智能数据可视化好帮手 爱图表是镝数科技旗下的一款智能数据可视化工具&#xff0c;它能让复杂的数字和报表变得直观又好懂。接入了先进的DeepSeek 系列 AI 模型&#xff0c;它不仅会做…

ENVI系列教程(四)——图像几何校正

目录 1 概述 1.1 控制点选择方式 1.2 几何校正模型 1.3 控制点的预测与误差计算 2 详细操作步骤 2.1 扫描地形图的几何校正 2.1.1 第一步:打开并显示图像文件 2.1.2 第二步:启动几何校正模块 2.2 Landsat5 影像几何校正 2.2.1 第一步:打开并显示图像文件 2.2.2 第…

STM32-FreeRTOS操作系统-消息队列

引言在嵌入式开发领域&#xff0c;STM32与FreeRTOS的结合应用极为广泛。本文将探讨如何在STM32上使用FreeRTOS实现消息队列功能&#xff0c;助力高效任务通信与系统协作。消息队列定义消息队列是一种在 FreeRTOS 中用于任务间通信的机制。它允许任务将消息发送到队列中&#xf…

【开题答辩全过程】以 C语言程序设计课程网站为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

手机上有哪些比较好用的待办事项提醒工具

在快节奏的现代工作中&#xff0c;我们每天都要面对大量的任务与事务。从项目截止日期、客户会议&#xff0c;到日常的工作安排&#xff0c;琐碎的事项容易让人顾此失彼。 手机待办事项工具早已突破传统“记事本”的局限&#xff0c;成为移动办公场景下的效率核心。它们通过任务…

Mysql数据库事务全解析:概念、操作与隔离级别

MySQL系列 文章目录MySQL系列一、什么是事务1.1事务的核心概念1.2、 事务的四大属性&#xff08;ACID&#xff09;1.2.1 原子性&#xff08;Atomicity&#xff09;1.2.2 一致性&#xff08;Consistency&#xff09;1.2.3 隔离性&#xff08;Isolation&#xff09;1.2.4 持久性&…

【MCU EEPROM开发教程】

简单来说把eeprom芯片当成一个传感器来使用&#xff0c;通过IIC/SPI等协议对芯片进行读写操作&#xff0c;具体的读写操作涉及到一些算法—怎么样读写更加快速&#xff0c;以及一些异常错误处理。 应用场景&#xff1a; 对于一些掉电也不能丢失的数据要存在eeprom/flash中&…

Docker将镜像搬移到其他服务上的方法

导出/加载镜像&#xff08;保留分层、标签&#xff09;和导出/导入容器快照&#xff08;仅文件系统&#xff0c;丢失镜像历史与标签&#xff09;。 一、把镜像打包带走&#xff08;推荐&#xff09; 适合把一个或多个镜像搬到离线/内网机器&#xff0c;保留分层与标签。 在源服…

Ubuntu 系统安装 Miniconda 完整方法与注意事项

一、完整安装步骤 1. 下载 Miniconda 安装包 Miniconda 安装包为 .sh 格式脚本,下载途径分两种: 方式 1:浏览器下载(适合新手) 访问 Miniconda 官方下载页,选择对应系统版本(Ubuntu 选 Miniconda3-latest-Linux-x86_64.sh),默认保存到用户目录的 ~/Downloads 文件夹…

【后端】数据库四大范式详细解析

梳理一下 MySQL&#xff08;或关系型数据库&#xff09;中的第一、二、三、四范式&#xff0c;这是数据库设计中非常重要的规范化理论。1️⃣ 第一范式 (1NF&#xff1a;First Normal Form)定义&#xff1a;字段具有原子性&#xff0c;不可再分。数据表中每一列都必须是不可分割…

HarmonyOS后台任务调度:JobScheduler与WorkManager实战指南

本文将深入探讨HarmonyOS 5&#xff08;API 12&#xff09;中的后台任务调度机制&#xff0c;重点讲解JobScheduler和WorkManager的使用方法、适用场景及最佳实践&#xff0c;帮助开发者实现高效、智能的后台任务管理。 1. 后台任务调度概述 HarmonyOS提供了两种主要的后台任务…

Prompt工程实践

你在写prompt时候&#xff0c;是不是总觉得大模型它不听话。要么答非所问、要么一堆废话。扒开思考过程仔细阅读时而觉得它聪明绝顶&#xff0c;时而又觉得它愚蠢至极。明明已经对了怎么又推理到错的地方去了&#xff0c;明明在提示词中提醒过了不要这么思考它怎么就瞎想了。这…