【动态规划】子数组系列二(数组中连续的一段)

news2024/11/15 6:48:31

子数组系列一(数组中连续的一段)

  • 1.等差数列划分
  • 2.最长湍流子数组
  • 3.单词拆分
  • 4.环绕字符串中唯一的子字符串

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.等差数列划分

题目链接: 413. 等差数列划分

题目分析:

在这里插入图片描述

如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。返回数组 nums 中所有为等差数组的 子数组 个数。

在这里插入图片描述

算法原理:

1.状态表示

经验 + 题目要求

以 i 位置为结尾,巴拉巴拉。
题目要求,求数组中为等差数组的子数组个数,也就是子数组中有多少个等差数列

dp[i] 表示:以 i 位置元素为结尾的所有子数组中有多少个等差数列。

2.状态转移方程

如果[a, b, c, d]已经构成一个等差数列,d后面在加一个e,与c、d、e构成等差数列,[a, b, c, d, e]也是构成等差数列。

dp[i] 表示:以 i 位置元素为结尾的所有子数组中有多少个等差数列。子数组要求是连续的,所以求 i 位置,要先去看 i -1 和 i - 2 的位置。

i - 2 位置元素设为a,i - 1 位置元素设为b、i 位置元素设为c

先考虑a、b、c是否构成一个等差数列。

如果abc能构成一个等差数列,那就是以ab为结尾的等差数列后面在加一个c,这些数列也是构成一个等差数列,以ab为结尾就相当于以b为结尾,以b为结尾的等差数列,就在dp[i-1]存着。别忘记 abc也能构成一个等差数列。

在这里插入图片描述
abc不能构成一个等差数列,即使a前面能构成等差数列,但是与 i 位置不连续,因此就构不成以 i 位置为结尾的等差数列

在这里插入图片描述

3.初始化

这里我们可以直接把dp[0] = dp[1] = 0

4.填表顺序

从左往右

5.返回值

注意并不是返回最后一个位置,题目要求找的是所有等差数列的个数,因此我们返回的是dp表所有元素的和。

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        // 1.创建 dp 表
        // 2.初始化
        // 3.填表
        // 4.返回值
        int n = nums.size();
        vector<int> dp(n);
        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;
        }

        int ret = 0;
        for(auto e : dp)
            ret += e;
        return ret;

    }
};

2.最长湍流子数组

题目链接: 978. 最长湍流子数组

题目分析:

在这里插入图片描述

给定一个整数数组 arr ,返回 arr 的 最大湍流子数组的长度 。

如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是 湍流子数组 。

湍流子数组到底什么东西,我们举个例子,比如说下面,元素大小呈现一升一降的趋势这就是湍流数组。那就称这个数组是湍流子数组。

在这里插入图片描述

在这里插入图片描述
算法原理:

1.状态表示

经验 + 题目要求

以 i 位置为结尾,巴拉巴拉

要求找子数组中最大湍流长度。

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

在这里插入图片描述

根据最近的一步分析问题,设 i - 1 位置元素为 a,i 位置元素为 b。此时会有三种情况。

a > b i位置最后呈现下降趋势
a < b i位置最后呈现上升趋势
a == b i位置最呈现平稳趋势

a > b i位置最后呈现下降趋势,我们是不是要找到以 i - 1 位置为结尾呈现升序趋势的湍流数组啊。

但是我们的状态表示只是表示 以 i 位置元素为结束的所有子数组中,最大湍流数组的长度,并没有分是上升趋势还是下降趋势。因此一个状态表示不能满足现在的情况。所以我们要换状态表示。

在这里插入图片描述

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

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

在这里插入图片描述

2.状态转移方程

先来分析f表,
当a > b i 位置最后呈现下降趋势,但是f表要的是 i 位置最后呈现上升趋势,别忘记本身也可以是湍流子数组,因此是1

当a < b i 位置最后呈现上升趋势,去 i - 1位置找到以 i - 1位置为结尾最后呈现下降趋势的最长湍流数组的长度这个就在 g[i - 1]存着,最后别忘记加上1

当 a == b,本身也可以是湍流子数组,因此是1

g表分析和f表分析一样,可以自己试着分析一下。

当a > b i 位置最后呈现下降趋势,去 i - 1位置找到以 i - 1位置为结尾最后呈现上升趋势的最长湍流数组的长度这个就在 f[i - 1]存着,最后别忘记加上1

当a < b i 位置最后呈现上升趋势,但是g表要的是 i 位置最后呈现下降趋势,别忘记本身也可以是湍流子数组,因此是1

当 a == b,本身也可以是湍流子数组,因此是1

在这里插入图片描述

3.初始化

填第一个位置会越界,可以把第一个位置初始化,注意本身也可以是湍流子数组,因此第一个位置可以初始化为1,不过这里我们可以把数组初始化都为1,这样的话
f表 a > b 和 a == b ,g表 a < b 和 a == b,填表的时候就不用考虑了,反正f[i]和g[i]都已经初始化为1了。

在这里插入图片描述

4.填表顺序

从左往右,两个表一起填

5.返回值

我们要的是子数组中最大湍流数组的长度,因此是找两个表里面的最大值。

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        // 1.创建 dp 表
        // 2.初始化
        // 3.填表
        // 4.返回值

        int n = arr.size();
        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.单词拆分

题目链接: 139. 单词拆分

题目描述:

在这里插入图片描述

算法原理:

1.状态表示

经验 + 题目要求

以 i 位置为结尾,巴拉巴拉
题目要求看字符串能否被字典中单词拼接而成

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

2.状态转移方程

根据最近一个位置的情况,来划分问题

i的位置是最后一个单词的位置,那最后一个单词是什么样子呢?可能本身就是最后一个单词,或者往前几位然后和 i 位置组成一个单词。又或者前面所有位置构成最后一个单词。这样划分可以把0-i位置字符串划分成两部分,前面部分字符串+后面的单词。

如果我们能确定前面部分字符串能够拼接而成并且后面的单词在字典中,那 0 - i 位置字符串肯定能被拼接而成。

但是我们并不知道最后一个单词起始下标在哪里,因此设一个 j 为最后一个单词的起始位置下标。(0 <= j <= i)

所以我们的状态转移方程就有了

在这里插入图片描述

3.初始化

填第一个位置,dp[j -1]会越界,因此多开一个空间

  1. 虚拟节点里面的值要保证后序填表正确
  2. 下标的映射关系

虚拟节点如果给false,整个表不管字符串是什么样子都是false,因此给true。

下标的映射关系,对于普通数据我们都是下标减一然后才能找到原数组,但是字符串这里有特殊技巧,可以把原始字符前面多加一个辅助字符 ‘ ’ 如 s = ’ ’ + s, 那原始字符串有效字符是不是从下标1开始了,正好就和多开一个空间的dp对应起来了。 因为字符串涉及找子串的问题,如果不这样搞,找子串非常头疼,因为下标都不对应了。

在这里插入图片描述

4.填表顺序

从左往右

5.返回值

看整个字符串能否被拼接成功,所以返回dp表最后一个位置,dp[i]

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        // 1.创建 dp 表
        // 2.初始化
        // 3.填表
        // 4.返回值

        //优化
        unordered_set<string> us;
        for(auto& str : wordDict)
            us.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] && us.count(s.substr(j, i - j + 1)))
                {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[n];
    }
};

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

题目链接: 467. 环绕字符串中唯一的子字符串

题目分析:

在这里插入图片描述

给一个字符串s,返回字符串s中有多少非空子串在 base 中出现。

注意最后结果是去重的!

在这里插入图片描述

算法原理:

1.状态表示

经验 + 题目要求

以 i 位置为结尾,巴拉巴拉。

题目要求返回字符串s中有多少非空子串在 base 中出现。
以 i 位置为结尾我要先确定 以 i 位置为结尾的所有子串,要么是自己本身,要么就是和前面位置形成的子串,接下来找多少个在base中出现。所以状态表示:

在这里插入图片描述

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

2.状态转移方程

单独本身就是一个子串,和前面元素结合构成子串。所以dp[i]也分这两种情况:

长度为1,长度大于1

长度为1,在base出现一次

长度大于1,前面元素都有一个共同点,都是以 i - 1 位置元素为结尾形成的子串,那我先找到以 i - 1 位置元素为结尾所有子串在base中出现的次数,然后在加上 i 位置元素构成的子串,看新的子串在base中出现的次数不就可以了吗。而dp[i-1] 就是以 i - 1位置元素为结尾所有子串在base中出现的次数。然后如果 i 位置元素s[i - 1] == s[i] || (s[i - 1] == z && s[i] ==a)说明长度大于1以 i 位置结束的字符串也在base出现了,出现次数是dp[i - 1],不能在后面 + 1!前面出现次数加上i位置这个字符次数还是一样的。

在这里插入图片描述

3.初始化

我们可以把dp表里面的值都初始化为1,首先本身肯定子串。其次初始化为1的话,上面dp表长度为1 填表时就不要在考虑了,只需要看是否满足长度为1的条件就行了。

在这里插入图片描述

4.填表顺序

从左往右

5.返回值

题目要求返回s 中有多少 不同非空子串 也在 base 中出现次数,
而dp[i] 表示:以 i 位置元素为结尾的所有子串里面,有多少个在base中出现。
因此返回 dp 表里面所有元素的和。

但是这样并不对!

我们初始化为1之后,这个例子我们最终返回的是3,但是答案最终是2,是去过重的。

在这里插入图片描述

思考一下如何去重?相同子串只统计一次!

看下面例子,两个字符串都是以c为结尾,dp表里面存的值,肯定是短的子串小,长的子串大。并且长的子串包含了短的子串的,因此长的子串里面统计的次数也包含了短的子串,因此当相同字符结尾的dp表里面的值较大的累加上,较小的直接舍去因为里面全都是重复的。

如何保证相同字符结尾的 dp 值,我们取最大的呢?

  1. 创建一个大小为 26 的数组
  2. 里面的的值保存相应字符结尾的最大 dp 值即可

最后返回数组里面的和

在这里插入图片描述

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        // 1.创建 dp 表
        // 2.初始化
        // 3.填表
        // 4.返回值

        int n = s.size();
        vector<int> dp(n, 1);
        for(int i = 1; i < n; i++)
            if(s[i] - 1 == s[i - 1] || (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 x : hash) 
            sum += x;
        
        return sum;
    }
};

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

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

相关文章

【多场景应用】基于杰发科技AC7840x的Mini LED背光驱动设计

应用场景&#xff1a; 在汽车应用中&#xff0c;Mini LED背光驱动设计主要用于仪表盘、中控屏和车载娱乐系统等显示屏。这项技术可以显著提升显示效果&#xff0c;提供更高的亮度、更深的黑色和更广的色域&#xff0c;使得图像更加生动逼真&#xff0c;尤其在强光和宽温度范围…

【类模板】类模板的基本范式

一、类模板的概念 类模板与一般的模板一样&#xff0c;都是通过给定的模板参数&#xff0c;生成具体的类&#xff0c;也就是实例化一个特定的类。这个概念和函数模板差不多。 例如&#xff0c;在 C C C中的 s t d : : v e c t o r std::vector std::vector容器就是一个经典的…

AWS账号关闭后的影响:您需要知道的一切

亚马逊网络服务&#xff08;AWS&#xff09;作为全球领先的云计算平台&#xff0c;为众多企业和个人提供了便捷、高效的云服务。然而&#xff0c;当用户决定关闭其AWS账号时&#xff0c;可能会对其现有的服务和资源产生重大影响。我们九河云将通过本文将深入探讨AWS账号关闭后的…

树莓派3B驱动ST7735(Python)

一 环境准备 之前做了PICO驱动ST7735&#xff0c;这次再进一步&#xff0c;用树莓派3B来驱动。还是先上图。 最开始还是根据GPT的指引来做的。SPI的细节就不多说了&#xff0c;之前在PICO的时候说过了。 总线学习3--SPI-CSDN博客 二 实现细节 连接方式如下&#xff1a; VCC …

猛兽财经:AMD股票值得长期投资吗?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 过去三年对AMD来说可谓压力山大&#xff0c;由于个人电脑(PC)市场的疲软&#xff0c;AMD的股价一直承受着巨大的压力&#xff08;AMD的股价在过去三年中仅上涨了44%&#xff0c;远远低于费城半导体指数56%的涨幅&#xff…

快速申请公网、内网IP地址SSL证书

在当今社会&#xff0c;网络安全越来越重要。SSL证书不仅能够提供加密的数据传输&#xff0c;还能增强用户信任度&#xff0c;提升搜索引擎排名等。但是只有IP地址可以用来申请SSL证书吗&#xff1f;答案当然是可以的&#xff0c;而且申请也非常容易。下面是快速申请流程&#…

SpringBoot3核心特性-数据访问

目录 传送门前言整合SSM场景一、创建SSM整合项目二、配置数据源三、配置MyBatis四、CRUD编写五、自动配置原理六、快速定位生效的配置七、扩展&#xff1a;整合其他数据源1、Druid 数据源2、附录&#xff1a;示例数据库 传送门 SpringMVC的源码解析&#xff08;精品&#xff0…

如何通过工业交换机增加网络带宽?

在现代工业环境中&#xff0c;网络的稳定性和带宽的充足性显得尤为重要。随着设备的增加和数据流量的增长&#xff0c;单一的网络带宽往往难以满足企业对于高效数据传输的需求。因此&#xff0c;如何通过工业交换机来增加网络带宽&#xff0c;成为了技术人员关注的重点。 首先&…

新手使用住宅代理有哪些常见误区?

作为新手&#xff0c;在使用住宅代理时往往会陷入一些常见误区&#xff0c;这些误区不仅可能影响到使用效果&#xff0c;甚至可能会带来安全风险。今天将与大家探讨新手在使用住宅代理时可能会遇到的几个关键误区&#xff0c;并提供相应的解决方案。误区一&#xff1a;盲目追求…

「合诚」× 企企通SRM项目启动,高分子新材料和健康产业高新技术企业将奔赴数智采购新征程

为拉通产业链上下游&#xff0c;优化提升整体效率&#xff0c;帮助企业变革采购管理方式&#xff0c;推动化工新材料行业高质量发展&#xff0c;近日&#xff0c;合诚技术股份有限公司&#xff08;以下简称“合诚”&#xff09;携手企企通成功举办了SRM项目启动会&#xff0c;双…

微电网能量管理系统在企业光伏电站的应用

发展背景&#xff1a; 在全球不可再生能源稀缺的背景下各个国家都在大力发展可再生能源&#xff0c;因此光伏行业应需而生且迅速发展了起来。能源转型中的光伏储能是指将光伏发电与储能技术相结合&#xff0c;以解决太阳能发电的间歇性和不稳定性问题&#xff0c;实现更稳定、…

怎么使用PPT倒计时插件?这款在线PPT工具,堪称办公必备!

在进行ppt演示时&#xff0c;为了更好地把控演示时间&#xff0c;有些人会在演示的同时设置一个倒计时&#xff0c;但Office的ppt本身没有提供倒计时功能&#xff0c;想要实现这一目的&#xff0c;就得在现有的基础上安装倒计时插件。 ppt倒计时插件 目前可用的免费ppt倒计时…

如何判断全面预算和EPM软件架构是否符合技术规范?

以全面预算管理软件为代表的企业绩效管理EPM软件&#xff0c;已经日益成为企业数字化智慧化管理的核心软件系统。国际企业采用了30多年的EPM系统&#xff0c;也逐渐被国内企业所熟识。全面预算管理软件的作用不仅仅是预算编报&#xff0c;还是整个企业实现高效经营分析和快速决…

基于PCL实现RGB-D图像转换为点云

原理: RGB 和 depth图已经对齐了,也就是 depth 图中某个位置的深度值在 RGB图中同样的位置处就是它对应的颜色。假设相机内参矩阵为: 则RGB-D图像转换为点云代码如下: for (int m = 0; m < depth.rows; m++)for (int n = 0

英飞凌HSM内核开发-CSM模块配置

CsmGeneral CsmJob CsmKey CsmQueue CsmPrimitive

最新保姆级教程

如何使用 WildCard 服务注册 Claude3 随着 Claude3 的震撼发布&#xff0c;最强 AI 模型的桂冠已不再由 GPT-4 独揽。Claude3 推出了三个备受瞩目的模型&#xff1a;Claude 3 Haiku、Claude 3 Sonnet 以及 Claude 3 Opus&#xff0c;每个模型都展现了卓越的性能与特色。其中&a…

Python+Selenium 通过添加cookies或token解决网页上验证码登录问题

cookie或token可以保存登录信息&#xff0c;当我们拿到cookie后&#xff0c;可以通过向浏览器发送cookie中记录的数据&#xff0c;直接变成登录状态&#xff0c;不需要再登录。 下面举个栗子 1、先把正常的登录方式码一下&#xff1a; browser webdriver.Chrome(executable_…

颈动脉斑块的MR图像分割

颈动脉斑块的MR图像分割是一个复杂的图像处理过程&#xff0c;它结合了医学影像学和计算机视觉技术。以下是一个基于一般流程的描述&#xff0c;包括可能的步骤和示例代码&#xff08;使用Python和OpenCV库&#xff09;&#xff0c;但请注意&#xff0c;实际应用中可能需要针对…

心脑血管科曹启富主任医师:血压高降不下来?找准这三个方向真的降下来了

高血压&#xff0c;这一影响超过3亿国人健康的慢性病&#xff0c;常常让人倍感困扰。尽管在医生的指导下科学用药&#xff0c;并通过调整饮食和生活习惯&#xff0c;大多数患者都能将血压控制在适宜水平&#xff0c;但在日常生活中&#xff0c;我们仍可能遇到血压突然升高的情况…

NFV架构

1&#xff09;NFV的背景 来自IT界的启示&#xff0c;给网络产业带来了网络架构和设备架构两个层面的思考。网络架构层面引入对SDN控制器的思考&#xff0c;设备架构层面引入对设备部署形态的思考&#xff08;NFV&#xff09;。 网络功能虚拟化被称为NFV&#xff08;Network Fu…