【动态规划】C++ dp子数组问题(最大/最长:环形/子数组和、乘积最大/为正数、单词拆分、子串)

news2025/3/12 1:44:09

文章目录

  • 1. 前言 - 理解动态规划算法
  • 2. 例题
    • 最大子数组和
  • 3. 算法题
    • 3.1_环形子数组的最大和
    • 3.2_乘积最大子数组
    • 3.3_乘积为正数的最长子数组长度
    • 3.4_等差数列划分
    • 3.5_最长湍流子数组
    • 3.6_单词拆分
    • 467.环绕字符串中唯一的子字符串

1. 前言 - 理解动态规划算法

关于 动态规划的理解 与例题,点击👇

【动态规划】C++解决斐波那契模型题目(三步问题、爬楼梯、解码方法…)

有了上面的经验,我们来解下面 子数组问题

2. 例题

下面的例题帮助我们理解子数组类问题的解法:

最大子数组和

在这里插入图片描述

  • 题意分析
    1. 题目要求找一个连续子数组 其总和最大。
    2. 要求很直白,我们根据要求设置状态表示即可:

在这里插入图片描述

在这里插入图片描述

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n + 1); // 创建dp数组
        // dp[0] = 0; 默认为0
        
        int ret = INT_MIN;
        for(int i = 1; i <= n; ++i)
        {
            dp[i] = max(nums[i - 1], dp[i-1] + nums[i - 1]);
            ret = max(ret, dp[i]);
        }
        
        return ret;
    }
};

3. 算法题

3.1_环形子数组的最大和

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求获得 环形子数组的最大和
    2. 主要问题在于如何对环形这一特点进行操作,我们可以分为两种情况:

在这里插入图片描述
有了上面两种状态,自然我们可以创建两个dp数组,下面进行状态表示设置:

在这里插入图片描述

随后进行 内容初始化以及其他细节

在这里插入图片描述

代码

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n = nums.size();
        // 创建dp数组
        vector<int> f(n + 1); // 记录到i位置时的子数组最大和
        auto g = f; // 记录到i位置时的子数组最小和

        // dp数组的计算
        int fmax = INT_MIN, gmin = INT_MAX, sum = 0;
        for(int i = 1; i <= n; ++i)
        {
            int x = nums[i - 1];

            f[i] = max(x, f[i-1] + x);
            fmax = max(fmax, f[i]); // f最大和
            
            g[i] = min(x, g[i-1] + x);
            gmin = min(gmin, g[i-1]); // g最小和

            sum += x; // 统计数组总和
        }

        // 当nums全部为负数时,sum - g[i]为0,而子数组最少要选择一位
        return sum == gmin ? fmax : max(fmax, sum - gmin);
    }
};

3.2_乘积最大子数组

在这里插入图片描述

思路

  • 题意分析
    1. 本题要求找到 连续子数组的最大乘积,乘积与和的差别在于,乘积需要考虑负数的影响,所以这里需要两个dp表

在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n+1); // 以i位置为结尾时,数组的最大乘积
        auto g = f; // 以i位置为结尾时,数组的最小乘积

        f[0] = g[0] = 1; // 初始化
        int ret = INT_MIN;
        for(int i = 1; i <= n; ++i)
        {
            int x = nums[i-1], y = f[i-1] * nums[i - 1], z = g[i-1] * nums[i - 1];
            f[i] = max(max(x, y), z);
            g[i] = min(min(x, y), z);
            ret = max(ret, f[i]);
        }

        return ret;
    }
};



3.3_乘积为正数的最长子数组长度

在这里插入图片描述
思路

  • 题意分析
    1. 题目要求找 乘积为正数 最长子数组的长度 ,注意这里要的是长度
    2. 乘积有正负之分,我们依然创建两个dp表

在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n = nums.size();
        vector<int>f(n + 1), g(n + 1); // 创建dp数组
        // 默认初始化为f[0] = g[0] = 0;

        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;
            }
            ret = max(ret, f[i]);
        }

        return ret;
    }
};

3.4_等差数列划分

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找到的 所有为等差数列的子数组,并返回总个数
    2. 根据要求我们可以直接设置dp状态表示:

在这里插入图片描述
在这里插入图片描述

代码

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n); // dp[i]: 以i为结尾的子数组中等差数列的个数
        // dp[0] = dp[1] = 0; // 默认为0,无需初始化

        int sum = 0; // 记录等差数列的个数
        for(int i = 2; i < n; ++i)
        {
            dp[i] = nums[i] - nums[i-1] == nums[i-1] - nums[i-2] ? dp[i-1] + 1 : 0;
            sum += dp[i];
        }

        return sum;
    }
};

3.5_最长湍流子数组

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找到所有的属于 的 湍流数组 的子数组的的 个数 ,什么是湍流数组?
    2. 如图所示,湍流数组可以理解为,递增递减关系是以上一下/一下一上的
      在这里插入图片描述
    3. 如何解决?可以看出来湍流数组实际上是有两种形态的,即对于最后一个状态,其为上升态或是下降态,我们可以以此创建两个dp表:

在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        int n = arr.size();
        // 创建dp数组 + 初始化
        // 以i为结尾的所有子数组中,为上升 / 下降状态的最长长度
        vector<int> f(n, 1), g(n, 1);
        
        int ret = 1;
        for(int i = 1; i < n; ++i)
        {
            if(arr[i - 1] < arr[i]) f[i] = g[i-1] + 1;
            else if(arr[i-1] > arr[i]) g[i] = f[i-1] + 1;
            ret = max(ret, max(f[i], g[i]));
        }

        return ret;
    }
};

3.6_单词拆分

在这里插入图片描述

思路

  • 题意分析
    1. 根据题目要求我们可以设置状态表示:

在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        // 优化:利用哈希表存储数组中的单词
        unordered_set<string> hash;
        for(auto& word : wordDict)
            hash.insert(word);

        int n = s.size();
        vector<bool> dp(n + 1, false); // dp[i] 表示前 i 个字符能否被拆分
        dp[0] = true; // 空字符串可以被拆分

        for (int i = 1; i <= n; ++i)
            for (int j = i - 1; j >= 0; --j) 
            {
                if (dp[j] && hash.count(s.substr(j, i - j))) 
                {
                    dp[i] = true;
                    break;
                }
            }

        return dp[n];
    }
};


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

在这里插入图片描述

思路

  • 题意分析

    1. 根据题目:base为一个无限环绕的26个字母的字符串,需要找到给定字符串中 的 属于base 的非空子串的个数
    2. 如上根据题目要求,我们可以设置状态表示:

    在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        int n = s.size();
        // 创建dp数组 + 初始化
        vector<int> dp(n, 1); // dp[i]: 以i为结尾的所有子数组中,在base里的数组个数
        // 填数组
        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]; // 相当于dp[i] = dp[i-1]+1
    
        // 哈希表用于找:以该字符为结尾的,最长的数组中,在base 的数组个数
        int hash[26] = {0};
        for(int i = 0; i < n; ++i)
            hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);

        // 返回值
        int ret = 0;
        for(int num : hash)
            ret += num;

        return ret;
    }
};

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

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

相关文章

chrome 浏览器 f12 如何查看 websocket 消息?

1. 打开目标页面 2. f12--》网络--》WS&#xff0c;然后刷新页面( 如果不刷页面&#xff0c;就会看不到 websocket 请求&#xff0c;因为 websocket 是长连接&#xff0c;页面加载后只发出一次连接请求&#xff0c;不像 http 接口&#xff0c;不用刷新页面&#xff0c;待会儿也…

常见UI设计模式有哪些?从小白到资深必学

通过了解如何以及何时使用&#xff0c;每种 UI 设计模式都有其特定的目的&#xff0c;可以创建一个一致高效的界面。UI 设计模式为用户界面设计者提供了一种通用语言&#xff0c;并为网站和应用程序的用户提供了一致性。本指南&#xff0c;即时设计总结了 UI 设计模式和 UI 设计…

百种提权及手段一览系列第5集

特权升级的危险是显而易见的。通过提升权限&#xff0c;攻击者可以绕过网络安全措施&#xff0c;从而损害数据完整性、机密性和系统可用性。对于组织而言&#xff0c;这可能会导致数据泄露、系统停机以及潜在的法律和声誉后果。识别权限升级的迹象并部署预防性网络安全措施对于…

【团体程序设计天梯赛 往年关键真题 详细分析完整AC代码】L2-009 抢红包(排序) L2-010 排座位 (dfs)

【团体程序设计天梯赛 往年关键真题 详细分析&完整AC代码】搞懂了赛场上拿下就稳 【团体程序设计天梯赛 往年关键真题 25分题合集 详细分析&完整AC代码】&#xff08;L2-001 - L2-024&#xff09;搞懂了赛场上拿下就稳了 【团体程序设计天梯赛 往年关键真题 25分题合…

Vue2学习笔记(尚硅谷天禹老师)

目录 一、入门案例 二、模板语法 三、数据绑定 四、el和data的两种写法 五、MVVM模型 六、Object.defineproperty方法 七、Vue中响应式原理 八、数据代理 九、methods配置项 十、Vue中的事件处理 十一、Vue中的键盘事件 十二、计算属性 十三、监视属性watch 十四、绑定Class样式…

《系统架构设计师教程(第2版)》第10章-软件架构的演化和维护-01-软件架构演化概述

文章目录 1. 演化的重要性2. 架构演化示例 教材中&#xff0c;本节名为&#xff1a;“软件架构演化和定义的关系” 1. 演化的重要性 演化目的&#xff1a;维持软件架构自身的有用性 为什么说&#xff0c;软件架构是演化来的&#xff0c;而不是设计来的&#xff1f; 软件架构的…

N元语言模型

第1关&#xff1a;预测句子概率 任务描述 本关任务&#xff1a;利用二元语言模型计算句子的概率 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.条件概率计算方式。 2.二元语言模型相关知识。 条件概率计算公式 条件概率是指事件A在事件B发生的条件下发…

麒麟龙芯loongarch64 electron 打包deb包

在麒麟龙芯&#xff08;loongarch64&#xff09;电脑上 使用electron 开发桌面应用。之前用electron-packager 打包出来的是文件夹 是 unpack 包。现在需要打包deb包&#xff0c;依据开发指南开始打包。 在项目文件夹下 打开终端 输入 npm run packager 先打包unpack包 然后…

AIGC算法3:Attention及其变体

1.Attention Attention是Transformer的核心部分&#xff0c;Attention机制帮助模型进行信息筛选&#xff0c;通过Q&#xff0c;K&#xff0c;V,对信息进行加工 1.1 attention计算公式 Attention ⁡ ( Q , K , V ) softmax ⁡ ( Q K T d k ) V \operatorname{Attention}(Q, K…

Fisher 准则分类

目录 一、什么是Fisher 准则 二、具体实例 三、代码实现 四、结果 一、什么是Fisher 准则 Fisher准则&#xff0c;即Fisher判别准则&#xff08;Fisher Discriminant Criterion&#xff09;&#xff0c;是统计学和机器学习中常用的一种分类方法&#xff0c;由统计学家罗纳…

【golang学习之旅】Go 的基本数据类型

系列文章 【golang学习之旅】报错&#xff1a;a declared but not used 目录 系列文章总览布尔型&#xff08;bool&#xff09;字符串型&#xff08;string&#xff09;整数型&#xff08;int、uint、byte、rune&#xff09;浮点型&#xff08;float32、float64&#xff09;复…

网络安全之防范钓鱼邮件

随着互联网的快速发展&#xff0c;新的网络攻击形式“网络钓鱼”呈现逐年上升的趋势&#xff0c;利用网络钓鱼进行欺骗的行为越来越猖獗&#xff0c;对互联网的安全威胁越来越大。网络钓鱼最常见的欺骗方式就是向目标群体发送钓鱼邮件&#xff0c;而邮件标题和内容&#xff0c;…

类的六个构造函数相关干货

构造函数 特点 1.名字与类名相同 2.无返回值 3.对象实例化的时候编译器自动调用这个函数 4.构造函数可以重载&#xff08;无参构造函数&#xff0c;拷贝构造等&#xff09; 5.如果类中没有显式定义构造函数&#xff08;深拷贝&#xff09;&#xff0c;则编译器会自动生成一个…

OpenSPG v0.0.3 发布,新增大模型统一知识抽取图谱可视化

基于非结构化文档的知识构建一直是知识图谱大规模落地的关键难题之一&#xff0c;4 月 23 日&#xff0c;OpenSPG 发布 v0.0.3 版本&#xff0c;正式发布了大模型统一知识抽取功能&#xff0c;可大幅降低领域知识图谱的构建成本。还可用于增强大模型缓解幻觉并提升稳定性&#…

Spring Boot中判断轨迹数据是否经过设置的打卡点,且在PGSQL中把点拼接成线,判断某个点是否在线上或在线的50米范围内

问题描述 轨迹数据判断是否经过打卡点&#xff0c;轨迹数据太多&#xff0c;循环判断的话非常消耗内存。解决办法只需要把所有轨迹数据点拼成了一条线&#xff0c;然后只需要循环打卡点即可&#xff0c;打卡点不多&#xff0c;一般不会超过100个&#xff0c;如果多的话&#x…

C++高级特性:异常概念与处理机制(十四)

1、异常的基本概念 异常&#xff1a;是指在程序运行的过程中发生的一些异常事件&#xff08;如&#xff1a;除数为0&#xff0c;数组下标越界&#xff0c;栈溢出&#xff0c;访问非法内存等&#xff09; C的异常机制相比C语言的异常处理&#xff1a; 函数的返回值可以忽略&…

C++ | Leetcode C++题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; class Solution { public:int firstMissingPositive(vector<int>& nums) {int n nums.size();for (int i 0; i < n; i) {while (nums[i] > 0 && nums[i] < n && nums[nums[i] - 1] ! nums[i]) {swap(…

助力实现更可持续未来的智能解决方案:AI如何改变世界

人工智能已然成为今年的热门话题。由于生成式AI应用的快速采用&#xff0c;新闻头条充斥着有关AI如何彻底改变我们的政策制定、就业和经济走向的预测。您知道AI也是我们应对各种可持续发展挑战的先锋吗&#xff1f;AI通过分析大量数据&#xff0c;并提供有用的见解和工具&#…

Python | Leetcode Python题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; class Solution:def isMatch(self, s: str, p: str) -> bool:def allStars(st: str, left: int, right: int) -> bool:return all(st[i] * for i in range(left, right))def charMatch(u: str, v: str) -> bool:return u v or v…

半波整流220V转正5V负-5V100mA恒压WT5101A

半波整流220V转正5V负-5V100mA恒压WT5101A WT5101A 是一款专为 Buck 和 Buck-Boost 拓扑而设计的高效、具有成本优势的离线恒压稳压器&#xff0c;内嵌有500V MOSFET。在降低系统成本的同时&#xff0c;这款稳压器只需少量的外部元件就能输出默认的5V电压。在轻负载条件下&…