动态规划<五> 子数组问题(含对应LeetcodeOJ题)

news2025/2/12 3:47:21

目录

引例

 经典LeetcodeOJ题

1.第一题

2.第二题

 3.第三题

 4.第四题

5.第五题

 6.第六题

 7.第七题

引例

OJ传送门 Leetcode<53> 最大子数组和

画图分析:

 使用动态规划解决

1.状态表示

dp[i]表示以i位置为结尾的所有子数组中的最大和

2.状态转移方程

子数组的问题可以根据最近的一步划分为长度为1和长度大于1这两种情况

3.初始化

根据状态转移方程,可能会访问越界的为i=0时,这里使用添加一个虚拟节点的方式来初始化

根据上面的dp表示,dp[0]的值应该是nums[0],那么为了不影响dp[0],此时dp[0-1]初始化为0,在多添加一个节点后,即dp[0-1+1]=0

往后在计算时要注意下标的映射关系

4.填表顺序  从左往右

5.返回值 因为求解的为子串,可能会以任意一个位置结束

所以返回值是整个dp表中的最大值 

具体代码:

int maxSubArray(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> dp(n+1);
        int ret=INT_MIN;
        for(int i=1;i<=n;++i) 
        {
            dp[i]=max(nums[i-1],nums[i-1]+dp[i-1]);//注意下标的映射关系
            ret=max(ret,dp[i]);
        } 
        return ret;
    }

 经典LeetcodeOJ题

1.第一题

OJ传送门 Leetcode<53> 环形子数组的最大和

画图分析:

 使用动态规划解决

1.状态表示

f[i]表示以i位置为结尾的所有子数组中的最大和

g[i]表示以i位置为结尾的所有子数组中的最小和

2.状态转移方程

和上面例题一样进行分析

 

3.初始化

和上面例题一样,添加一个虚拟节点来完成初始化,虚拟节点值为0

4.填表顺序 从左往右

5.返回值

 具体代码:

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

        return sum==gmin? fmax:max(fmax,sum-gmin);
    }

2.第二题

OJ传送门 Leetcode<152> 乘积最大子数组

画图分析:

 使用动态规划来解决

1.状态表示---根据经验(以xx位置为结尾,xxxx)+题目要求

dp[i]表示以i位置为结尾的所有子数组中的最大乘积

此时来分析状态转移方程的话,就会发现当nums[i]<0时,在前面需要求的是最小乘积

当nums[i]>0时,在前面求的是最大乘积,此时状态表示显然不能支持,就需要多加一维或者分开写

f[i]表示以i位置为结尾的所有子数组中的最大乘积

g[i]表示以i位置为结尾的所有子数组中的最小乘积

2.状态转移方程  不用考虑nums[i]=0的情况,选择0的话,乘积也都是0,在vector初始化是本来都是0

3.初始化--增加一个虚拟节点

根据状态表示及状态转移方程分析,将f[0]=g[0]=1

4.填表顺序  从左往右

5.返回值  f表中的最大值

具体代码:

int maxProduct(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n+1),g(n+1);
        f[0]=g[0]=1;
        int ret=INT_MIN;
        for(int i=1;i<=n;++i)
        {
            int x=nums[i-1],y=g[i-1]*nums[i-1],z=f[i-1]*nums[i-1];
            f[i]=max(x,max(y,z));
            g[i]=min(x,min(y,z));
            ret=max(ret,f[i]);
        }
        return ret;
    }

 3.第三题

OJ传送门 Leetcode<1567> 乘积为正数的最长子数组长度

画图分析:

 使用动态规划解决

1.状态表示  nums[i]可正可负,求子数组的积或和所以一般是要定两个状态

f[i]表示以i位置为结尾的所有子数组中乘积为正数的最长长度

g[i]表示以i位置为结尾的所有子数组中乘积为负数的最长长度

2.状态转移方程

3.初始化----添加一个虚拟节点来辅助初始化

分析状态表示及状态转移方程,f[0],g[0]进行判断,因此填的值都为0

4.填表顺序  从左往右

5.返回值    f表中的最大值

具体代码:

int getMaxLen(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n+1),g(n+1);
        int ret=INT_MIN;
        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;
            }
            ret=max(ret,f[i]);//更新结果
        }
        return ret;
    }

 4.第四题

OJ传送门 Leetcode<413> 等差数列划分

画图分析:

使用动态规划解决

1.状态表示  -----经验+题目要求

 dp[i]表示以i结尾的所有子数组中有多少个等差数列

2.状态转移方程

先补充一个小知识,若a,b,c,d,e是等差数列,在e的后面添加元素f,f与e的之差和前面的任意两个元素的差相等,此时就能说明,a,b,c,d,e,f成等差数列

3.初始化

根据状态转移方程及状态表示,可以将dp[0],dp[1]进行初始化,因为构成等差数列的最少元素为3个,因此dp[0]=dp[1]=0

4.填表顺序    从左往右

5.返回值   因为题目求的为等差数组的个数,因此结尾位置不确定,得统计整个dp表 

具体代码 :

int numberOfArithmeticSlices(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> dp(n);
        int sum=0;
        for(int i=2;i<n;++i)
        {
            dp[i]=nums[i-1]-nums[i]==nums[i-2]-nums[i-1]? dp[i-1]+1:0;
            sum+=dp[i];
        }

        return sum;
    }

5.第五题

OJ传送门 Leetcode<978> 最长湍流子数组

画图分析:

使用动态规划解决

1.状态表示

dp[i]表示以i位置为结尾的所有子数组中,最长的湍流子数组的长度

但在以最近的一步来划分子问题时,会发现nums[i]与nums[i-1]的大小关系有三种

但要形成湍流数组的话,就需要以nums[i-1]为结尾时的状态是上升还是下降的

 

这就说明一个状态不能解决问题,就需要再创建一个状态来表示

f[i]表示以i位置元素为结尾的所有子数组中,最后呈现"上升" 状态的湍流子数组的最大长度

g[i]表示以i位置元素为结尾的所有子数组中,最后呈现"下降" 状态的湍流子数组的最大长度

2.状态转移方程--根据nums[i]与nums[i-1]的大小关系分开处理

3.初始化

可能会发生越界的为g[0],f[0]这时根据题意及状态表示,自己一个元素即可以是上升也可以是下降的,因此初始化为1,为了简化状态转移方程来填dp表,可以将dp数组中的元素都初始化为1,这时就不用考虑很多情况了

4.填表顺序   从左往右,两个表一起填

5.返回值  求的是最长湍流数组的长度,最后一个位置的状态不一定是上升还是下降的,因此返回指的是f,g两个表中的最大值

具体代码:

 int maxTurbulenceSize(vector<int>& nums) 
    {
        int n=nums.size();
        vector<int> f(n,1),g(n,1);
        int ret=1;
        for(int i=1;i<n;++i)
        {
            if(nums[i-1]<nums[i]) f[i]=g[i-1]+1;
            else if(nums[i-1]>nums[i]) g[i]=f[i-1]+1;

            ret=max(ret,max(f[i],g[i]));
        }
        return ret;
    }

 6.第六题

OJ传送门 Leetcode<139> 单词拆分

 画图分析:

使用动态规划解决

1.状态表示

dp[i]表示[0,i]区间的最长子串,能否被字典中的单词拼接而成

2.状态转移方程  以最近的一步来划分问题

3.初始化

当j=0时,说明整个字符串都被当成最后一个单词了,若单词在字典中,其结果为true

为方便初始化,可以添加一个虚拟的节点,在字符串中的话,就可以在头部拼接一个字符

4.填表顺序    从左往右

5.返回值     dp[n]

具体代码:

bool wordBreak(string s, vector<string>& wordDict) 
    {
        //先做一个优化
        unordered_set<string> hash;
        for(auto& str:wordDict) hash.insert(str);

        int n=s.size();
        vector<bool> dp(n+1);
        dp[0]=true;//保证后续的填表时正确的
        s='-'+s;//是原始字符串的下标统一+1
        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];
    }

 7.第七题

OJ传送门 Leetcode<467> 环绕字符串中唯一的子字符串

画图分析:

使用动态规划解决

1.状态表示

dp[i]表示以i位置元素为结尾的所有子串,在base中出现的个数

2.状态表示

3.初始化

根据题意及状态表示,每个字符都是会出现的,结果都是1,因此可以将dp表全部初始化为1

4.填表顺序   从左往右

5.返回值    此处应该注意重复结果的去重

如示例中的第二个cac,若直接返回dp表的和的话,就会错误

此时可以举个例子来分析一下

 具体代码:

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]=='a'&& s[i-1]=='z')) 
            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 x:hash) sum+=x;

        return sum;
    }

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

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

相关文章

前端实现PDF预览的几种选择(pdfjs-dist、react-pdf、pdf-viewer)

记录 PDF预览的选型 对于浏览器自带的PDF预览 如果能直接使用&#xff0c;那自然最好不过了&#xff0c;但考虑多种因素&#xff0c;比如权限问题&#xff0c;禁止用户去下载PDF、预览样式不统一&#xff08;不同浏览器PDF预览的实现不同&#xff09;&#xff0c;所有最终放弃…

小米路由器开启SSH,配置阿里云ddns,开启外网访问SSH和WEB管理界面

文章目录 前言一、开启SSH二、配置阿里云ddns1.准备工作2.创建ddns脚本3.添加定时任务 三、开启外网访问SSH和WEB管理界面1、解除WEB管理页面访问限制2.手动添加防火墙端口转发规则&#xff0c;开启外网访问WEB管理和SSH 前言 例如&#xff1a;随着人工智能的不断发展&#xf…

机器学习(三)-多项式线性回归

文章目录 1. 多项式回归理论2. python通过多项式线性回归预测房价2.1 预测数据2.2导入标准库2.3 导入数据2.4 划分数据集2.5 构建二次多项式特征&#xff08;1, x, x^2&#xff09;2.6 导入线性回归模块2.7 对测试集进行预测2.8 计算均方误差 J2.9 计算参数 w0、w1、w22.10 可视…

【再学javascript算法之美】前端面试频率比较高的基础算法题

基础算法题练习代码&#xff0c;看看能做出几道题 代码实现 找出字符串中出现次数最多的字符 const array "cncnansdnajsadnjasndjnasjdnjj";// 找出出现次数最多的字符 let obj {}; for (let index 0; index < array.length; index) {const element array[…

芯产品|暴雨推出基于兆芯晶片的新品台式机

近期&#xff0c;基于兆芯开先KX-7000系列处理器&#xff0c;暴雨推出新品桌面整机TSJ200-ZX&#xff0c;凭借开先KX-7000系列处理器强劲的性能表现和优异的兼容性&#xff0c;将为行业信创深入发展增添更多的活力和能量。 暴雨TSJ200-ZX是针对政务办公&#xff0c;金融机构和…

echarts进度仪表盘形式

const pointerData 55; // 仪表指针数据const steps 10; // 总共10个步骤 const borderColor {colorStops: [{offset: 0,color: rgba(208, 244, 255, 1)}, {offset: 1,color: rgba(35, 190, 240, 1)}] }; // 边框颜色// 使用数组和循环动态生成颜色数组 const axisLinecolor…

代码随想录-笔记-其八

让我们开始&#xff1a;动态规划&#xff01; 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; class Solution { public:int climbStairs(i…

线性回归a

训练数据 求平方损失的平均值1/n&#xff0c;目标求解w&#xff0c;b使得损失函数最小 显示解

学习记录—正则表达式-基本语法

正则表达式简介-《菜鸟教程》 正则表达式是一种用于匹配和操作文本的强大工具&#xff0c;它是由一系列字符和特殊字符组成的模式&#xff0c;用于描述要匹配的文本模式。 正则表达式可以在文本中查找、替换、提取和验证特定的模式。 本期内容将介绍普通字符&#xff0c;特殊…

利用AI优化SEO关键词提升网站流量的有效策略

内容概要 在数字化时代&#xff0c;网站流量的增加对于任何企业或个人至关重要。为了在竞争激烈的市场中吸引更多用户&#xff0c;优化网站的SEO关键词显得尤为重要。随着人工智能技术的迅猛发展&#xff0c;它在SEO领域的应用也逐渐渗透&#xff0c;为关键词优化提供了新的可…

敏捷开发05:Sprint Planning 冲刺计划会议详细介绍和用户故事拆分、开发任务细分

Sprint Planning 冲刺计划会议简介 Sprint Planning &#xff08;冲刺计划会议&#xff09;&#xff0c;又叫规划会议。此会议通过 Scrum 团队的集体沟通讨论&#xff0c;确定接下来的 Sprint 中要完成的待开发项&#xff0c;把它们组成一个 Sprint Backlog。这些待开发项都是…

极简容器云WeKube快速体验

极简容器云WebKube快速体验 WeKube是什么&#xff1f; 概述 WeKube 是一个基于 Kubernetes 构建的极简Serverless容器服务&#xff0c;它提供了一个简单直观的方式来部署、管理和监控容器化的应用程序。WeKube 的目标是让用户无需关心底层基础设施的具体细节&#xff0c;而是…

Java开发经验——数据库开发经验

摘要 本文主要介绍了Java开发中的数据库操作规范&#xff0c;包括数据库建表规范、索引规约、SQL规范和ORM规约。强调了在数据库设计和操作中应遵循的最佳实践&#xff0c;如字段命名、数据类型选择、索引创建、SQL语句编写和ORM映射&#xff0c;旨在提高数据库操作的性能和安…

ovirt-engine登录报错

ovirt-engine登录报错 注&#xff1a;用户名不是admin&#xff0c;而是adminlocalhost

windows nmake 安装openssl

windows nmake 编译和安装 openssl 本文提供了在Windows环境下安装OpenSSL的详细步骤&#xff0c;包括下载Perl、NASM和VisualStudio&#xff0c;配置环境变量&#xff0c;使用PerlConfigure设置平台&#xff0c;通过nmake进行编译、测试和安装。整个过程涉及32位和64位版本的选…

智能家居实训室中,STC单片机驱动的“互联网+”智能家居系统设计

一、引言 随着经济的快速发展&#xff0c;人们对家居环境的智能化、网络化需求日益增强&#xff0c;智能家居的研究也因此受到了国内外相关机构的广泛关注。STC单片机凭借其卓越的性能和广泛的应用领域&#xff0c;成为了智能家居系统设计的优选方案。作为一种先进的微控制器&…

分析排名靠前的一些自媒体平台,如何运用这些平台?

众所周知&#xff0c;现在做网站越来越难了&#xff0c;主要的原因还是因为流量红利时代过去了。并且搜索引擎都在给自己的平台做闭环改造。搜索引擎的流量扶持太低了。如百度投资知乎&#xff0c;给知乎带来很多流量扶持&#xff0c;也为自身内容不足做一个填补。 而我们站长…

[计算机网络]OSPF协议

开放最短路径优先OSPF 1&#xff09;OSPF的工作方式 1>和谁交换消息 使用洪泛法&#xff0c;向本自治系统的所有路由器发送消息。 2>交换什么消息 发送的消息就是与本路由器相邻的所有路由器的链路状态&#xff0c;但这只是路由器所知道的部分信息。 链路状态就是说…

攻防世界PWN刷题笔记(引导模式)1-3

感谢组长和其他高手让我入门学pwn&#xff0c;个人感觉做题和看课程应该一块推进&#xff0c;光看课程&#xff0c;容易疲乏&#xff0c;而且缺乏经验。只做题&#xff0c;学的知识缺乏体系&#xff0c;因此决定立志每天看课&#xff0b;做题&#xff08;先保证不挂科的前提下&…

【机器学习案列】车牌自动识别系统:基于YOLO11的高效实现

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…