📚 力扣每日一题–2025.7.16

📚 3201. 找出有效子序列的最大长度 I(中等)

今天我们要解决的是力扣上的第 3201 题——找出有效子序列的最大长度 I。这道题虽然标记为中等难度,但只要掌握了正确的思路,就能轻松解决!

📝 题目描述

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

🤔 思路分析

核心需求推导过程 📝

题目要求子序列中所有相邻元素之和的奇偶性必须相同,即:

(sub[0] + sub[1]) % 2 == (sub[1] + sub[2]) % 2 == ... == (sub[x-2] + sub[x-1]) % 2

这个条件可以从两个角度理解:

  1. 数学本质:所有相邻元素对的和具有相同的奇偶性
  2. 序列特性:子序列中的元素必须形成一种特定的奇偶性模式

通过进一步推理,我们可以得出两种基本的有效序列模式:

模式一:全同奇偶性序列

  • 当要求相邻元素之和为偶数时,所有元素必须具有相同的奇偶性
  • 证明:若 a+b 为偶数且 b+c 为偶数,则 a 和 b 奇偶性相同,b 和 c 奇偶性相同,因此 a 和 c 奇偶性相同,以此类推

模式二:交替奇偶性序列

  • 当要求相邻元素之和为奇数时,元素必须交替出现奇偶性
  • 证明:若 a+b 为奇数且 b+c 为奇数,则 a 和 b 奇偶性不同,b 和 c 奇偶性不同,因此 a 和 c 奇偶性相同,形成交替模式

让我们通过具体示例验证这些模式:

  • 全奇序列 [1,3,5,7]:所有相邻和都为偶数 (4,8,12),符合模式一
  • 奇偶交替序列 [1,2,3,4]:所有相邻和都为奇数 (3,5,7),符合模式二
  • 混合序列 [1,2,2,3]:相邻和为 3(奇)、4(偶)、5(奇),不符合任何模式

所以,有效子序列有两种基本类型:

  1. 所有相邻元素之和都为偶数
  2. 所有相邻元素之和都为奇数

我们需要分别找出这两种类型的最长子序列,然后取较大值作为答案。

📑 解法探讨

方法一:暴力枚举(理解问题本质)

最直观的思路是枚举所有可能的子序列,检查它们是否有效,并记录最长有效子序列的长度。

这个官方的测试用例可以通过,但是提交的时候会超时,我这边只是给大家提供一个思路方法。

/*** 方法一:暴力枚举法* 思路:枚举所有可能的子序列起点和两种类型的有效子序列,*       构建并检查每个子序列的有效性,记录最长有效子序列长度* 注意:此方法仅用于理解问题本质,实际提交可能会超时*/
public class Solution {/*** 找出最长有效子序列的长度* @param nums 输入的整数数组* @return 最长有效子序列的长度*/public int maximumLength(int[] nums) {int n = nums.length;// 边界情况处理:空数组或单元素数组,直接返回数组长度if (n <= 1) return n;// 最小有效子序列长度为1(单个元素本身就是有效子序列)int maxLen = 1;// 枚举所有可能的子序列起点for (int i = 0; i < n; i++) {// 尝试两种类型的有效子序列// type=0: 相邻元素之和为偶数// type=1: 相邻元素之和为奇数for (int type = 0; type <= 1; type++) {int len = 1;          // 当前子序列长度,至少包含起点元素int last = nums[i];   // 记录子序列最后一个元素// 从起点后一个元素开始构建子序列for (int j = i + 1; j < n; j++) {// 计算当前元素与子序列最后一个元素之和的奇偶性int sumMod = (last + nums[j]) % 2;// 如果符合当前类型要求,则加入子序列if (sumMod == type) {len++;last = nums[j]; // 更新子序列最后一个元素}}// 更新最长有效子序列长度maxLen = Math.max(maxLen, len);}}return maxLen;}
}

复杂度分析

  • 时间复杂度:O(n²),其中 n 是数组长度
  • 空间复杂度:O(1),只使用了常数额外空间
方法二:贪心算法(最优解法)

通过观察,我们可以发现两种类型的有效子序列具有明显的模式:

  1. 类型 0(相邻元素之和为偶数):所有元素必须具有相同的奇偶性

    • 全是偶数,或全是奇数
    • 最长长度 = max(偶数元素个数, 奇数元素个数)
  2. 类型 1(相邻元素之和为奇数):元素必须交替出现奇偶性

    • 奇偶奇偶…或偶奇偶奇…
    • 最长长度取决于两种模式中较长的一个

基于这些观察,我们可以设计出 O(n)时间复杂度的贪心算法:

/*** 方法二:贪心算法(最优解法)* 思路:通过分析有效子序列的两种模式,分别计算其最大长度*       类型0(相邻和为偶数):所有元素奇偶性相同,取较多的那种奇偶性元素个数*       类型1(相邻和为奇数):元素奇偶性交替出现,计算两种起始模式的最大长度* 时间复杂度:O(n),空间复杂度:O(1)*/
public class Solution {/*** 找出最长有效子序列的长度* @param nums 输入的整数数组* @return 最长有效子序列的长度*/public int maximumLength(int[] nums) {int n = nums.length;// 边界情况处理:空数组或单元素数组,直接返回数组长度if (n <= 1) return n;// 统计数组中奇数和偶数的个数int oddCount = 0, evenCount = 0;for (int num : nums) {if (num % 2 == 0) evenCount++;else oddCount++;}// 类型0的最大长度:所有元素奇偶性相同,取较多的那种int type0Max = Math.max(oddCount, evenCount);// 如果只有一种奇偶性,无法形成类型1的子序列(需要交替出现)if (oddCount == 0 || evenCount == 0) {return type0Max;}// 计算类型1的两种模式的最大长度// 模式1:以奇数开始的交替序列(奇-偶-奇-偶...)int type1Max1 = calculateAlternatingLength(nums, true);// 模式2:以偶数开始的交替序列(偶-奇-偶-奇...)int type1Max2 = calculateAlternatingLength(nums, false);// 取两种模式中的较大值int type1Max = Math.max(type1Max1, type1Max2);// 返回两种类型中的最大值return Math.max(type0Max, type1Max);}/*** 计算交替奇偶性序列的长度* @param nums 输入的整数数组* @param startWithOdd 是否以奇数开始* @return 交替序列的长度*/private int calculateAlternatingLength(int[] nums, boolean startWithOdd) {int length = 0;               // 序列长度boolean expectedOdd = startWithOdd; // 期望的下一个元素的奇偶性// 遍历数组,构建交替序列for (int num : nums) {boolean isOdd = num % 2 == 1; // 当前元素是否为奇数// 如果当前元素符合期望的奇偶性,则加入序列if (isOdd == expectedOdd) {length++;expectedOdd = !expectedOdd; // 切换期望的奇偶性}}return length;}
}

复杂度分析

  • 时间复杂度:O(n),只需遍历数组几次
  • 空间复杂度:O(1),只使用了常数额外空间
方法三:动态规划(更通用的解决方案)

这个也超时了,但是方法思路应该是这样的,之后我再想办法去优化一下。

我们也可以使用动态规划来解决这个问题,定义两个状态:

  • dp0[i]:以第 i 个元素结尾,且相邻元素之和为偶数的最长有效子序列长度
  • dp1[i]:以第 i 个元素结尾,且相邻元素之和为奇数的最长有效子序列长度
/*** 方法三:动态规划(更通用的解决方案)* 思路:定义两个状态数组,分别记录以每个位置结尾的两种类型子序列的最大长度*       dp[i][0]:以第i个元素结尾,相邻和为偶数的最长子序列长度*       dp[i][1]:以第i个元素结尾,相邻和为奇数的最长子序列长度* 时间复杂度:O(n²),空间复杂度:O(n)*/
public class Solution {/*** 找出最长有效子序列的长度* @param nums 输入的整数数组* @return 最长有效子序列的长度*/public int maximumLength(int[] nums) {int n = nums.length;// 边界情况处理:空数组或单元素数组,直接返回数组长度if (n <= 1) return n;// 动态规划数组定义:// dp[i][0]:以第i个元素结尾,且相邻元素之和为偶数的最长有效子序列长度// dp[i][1]:以第i个元素结尾,且相邻元素之和为奇数的最长有效子序列长度int[][] dp = new int[n][2];// 初始化:第一个元素本身就是长度为1的有效子序列dp[0][0] = 1; // 以第一个元素结尾的类型0子序列dp[0][1] = 1; // 以第一个元素结尾的类型1子序列int maxLen = 1; // 记录最长有效子序列长度// 从第二个元素开始计算for (int i = 1; i < n; i++) {// 默认情况下,子序列只包含当前元素,长度为1dp[i][0] = 1;dp[i][1] = 1;// 检查前面所有元素,看是否可以形成更长的有效子序列for (int j = 0; j < i; j++) {// 计算nums[j]和nums[i]之和的奇偶性int sumMod = (nums[j] + nums[i]) % 2;// 如果前面元素j和当前元素i的和的奇偶性为sumMod// 则可以将i添加到以j结尾的sumMod类型子序列后面if (dp[j][sumMod] + 1 > dp[i][sumMod]) {dp[i][sumMod] = dp[j][sumMod] + 1;}}// 更新最长有效子序列长度maxLen = Math.max(maxLen, Math.max(dp[i][0], dp[i][1]));}return maxLen;}
}

复杂度分析

  • 时间复杂度:O(n²),其中 n 是数组长度
  • 空间复杂度:O(n),需要存储 dp 数组

🚀 优化后的贪心算法

我们可以进一步优化贪心算法,只需要一次遍历就能计算出两种类型 1 子序列的最大长度:

/*** 方法四:优化后的贪心算法* 思路:在基础贪心算法的基础上,通过一次遍历同时计算两种类型1子序列的长度*       减少了遍历次数,进一步优化了性能* 时间复杂度:O(n),空间复杂度:O(1)*/
public class Solution {/*** 找出最长有效子序列的长度* @param nums 输入的整数数组* @return 最长有效子序列的长度*/public int maximumLength(int[] nums) {int n = nums.length;// 边界情况处理:空数组或单元素数组,直接返回数组长度if (n <= 1) return n;// 统计数组中奇数和偶数的个数int oddCount = 0, evenCount = 0;for (int num : nums) {if (num % 2 == 0) evenCount++;else oddCount++;}// 类型0的最大长度:所有元素奇偶性相同,取较多的那种int type0Max = Math.max(oddCount, evenCount);// 如果只有一种奇偶性,无法形成类型1的子序列(需要交替出现)if (oddCount == 0 || evenCount == 0) {return type0Max;}// 一次遍历同时计算两种类型1子序列的最大长度int type1OddStart = 0;  // 以奇数开始的交替序列长度(奇-偶-奇-偶...)int type1EvenStart = 0; // 以偶数开始的交替序列长度(偶-奇-偶-奇...)boolean expectOddForOddStart = true;  // 奇开始模式下期望的下一个奇偶性boolean expectOddForEvenStart = false; // 偶开始模式下期望的下一个奇偶性// 遍历数组,同时构建两种交替模式的子序列for (int num : nums) {boolean isOdd = num % 2 == 1; // 当前元素是否为奇数// 处理奇-偶-奇-偶...模式if (isOdd == expectOddForOddStart) {type1OddStart++;expectOddForOddStart = !expectOddForOddStart; // 切换期望的奇偶性}// 处理偶-奇-偶-奇...模式if (isOdd == expectOddForEvenStart) {type1EvenStart++;expectOddForEvenStart = !expectOddForEvenStart; // 切换期望的奇偶性}}// 类型1的最大长度为两种模式中的较大值int type1Max = Math.max(type1OddStart, type1EvenStart);// 返回两种类型中的最大值return Math.max(type0Max, type1Max);}
}

复杂度分析

  • 时间复杂度:O(n),只需一次遍历
  • 空间复杂度:O(1),只使用了常数额外空间

🔍 算法执行流程可视化

开始
统计奇数和偶数个数
计算类型0最大长度
是否同时有奇数和偶数?
返回类型0最大长度
计算两种类型1子序列长度
比较类型0和类型1的最大值
返回最终结果

📊 示例分析

让我们通过几个示例来理解算法的工作原理:

示例 1:nums = [1,2,3,4,5]

  • 奇数: 1,3,5 (count=3)
  • 偶数: 2,4 (count=2)
  • 类型 0 最大长度: max(3,2)=3
  • 类型 1(奇-偶-奇-偶…): 1,2,3,4,5 → 长度 5
  • 类型 1(偶-奇-偶-奇…): 2,3,4,5 → 长度 4
  • 最终结果: max(3,5)=5

示例 2:nums = [1,3,5,7]

  • 奇数: 1,3,5,7 (count=4)
  • 偶数: 0 (count=0)
  • 无法形成类型 1 子序列
  • 最终结果: 4

示例 3:nums = [1,2,2,2,2]

  • 奇数: 1 (count=1)
  • 偶数: 2,2,2,2 (count=4)
  • 类型 0 最大长度: max(1,4)=4
  • 类型 1(奇-偶-奇-偶…): 1,2 → 长度 2
  • 类型 1(偶-奇-偶-奇…): 2 → 长度 1
  • 最终结果: max(4,2)=4

💡 拓展思考

问题变体
  1. 如果要求子序列中相邻元素之和的余数等于某个特定值 k(而不只是所有余数相等),该如何解决?(这个是紧挨着的 力扣3202 题)
  2. 如果允许子序列中有一个"错误"(即有一处相邻元素之和的余数与其他不同),最长有效子序列的长度会是多少?
实际应用

这个问题在信号处理和模式识别中有实际应用。例如,在分析时间序列数据时,我们可能需要找到具有特定模式的最长子序列。

算法选择策略
  • 对于小规模数据,任何算法都能胜任
  • 对于大规模数据,贪心算法是最佳选择,因为它具有 O(n)时间复杂度
  • 动态规划方法虽然复杂度较高,但具有更好的通用性,可以解决更复杂的变体问题

📝 总结

本题考察了对子序列概念的理解以及贪心算法的应用。通过分析相邻元素之和的奇偶性规律,我们发现有效子序列有两种基本类型,并分别计算了它们的最大长度。

贪心算法是解决本题的最优选择,它基于以下关键洞察:

  1. 类型 0 子序列要求所有元素具有相同的奇偶性
  2. 类型 1 子序列要求元素的奇偶性交替出现

通过计算这两种类型的最大长度并取较大值,我们得到了问题的解。

希望今天的讲解能帮助你更好地理解这个问题和贪心算法的应用!如果你有任何疑问或想法,欢迎在评论区留言讨论。明天见!👋

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

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

相关文章

SFT:大型语言模型专业化定制的核心技术体系——原理、创新与应用全景

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 以下基于权威期刊、会议论文及技术报告&#xff0c;对监督微调&#x…

若依前后端分离框架配置多数据库表

若依前后端分离框架配置多数据库表1、配置application.yml2、注释掉application-druid.yml中的数据库3、在DataSourceType 中添加新增的数据库来源4、配置DruidConfig文件4、1新增注入方法&#xff0c;在DataSourceType类添加数据源枚举4、2在DruidConfig类dataSource方法添加数…

29.安卓逆向2-frida hook技术-逆向os文件(二)IDA工具下载和使用(利用ai分析so代码)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

[析]Deep reinforcement learning for drone navigation using sensor data

Deep reinforcement learning for drone navigation using sensor data 基于传感器数据的无人机导航深度强化学习方法 评价&#xff1a;MDP无记忆性&#xff0c;使用LSTM补足缺点。PPO解决新旧策略差距大的问题。 对于环境中的障碍物&#xff0c;设置增量课程&#xff0c;障碍…

SpringBoot项目启动报:java: 找不到符号 符号: 变量 log 的解决办法

问题&#xff1a;使用IDEA创建SpringBoot项目&#xff0c;在项目中使用 Slf4j 注解引入log日志后&#xff0c;启动项目&#xff0c;报如下错误&#xff1a;原因&#xff1a;网上找了很多博文&#xff0c;说是lombook依赖没有引入&#xff0c;但是我的pom.xml中已经引入 lombook…

HTML基础知识 二(创建容器和表格)

HTML 基础知识&#xff1a;创建容器和表格&#xff08;补充版&#xff09;HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础。容器元素用于组织内容&#xff0c;表格用于展示结构化数据&#xff0c;两者都是网页设计中不可或缺的部分。一、HTML 容器元素容器元素就…

多目标优化|HKELM混合核极限学习机+NSGAII算法工艺参数优化、工程设计优化,四目标(最大化输出y1、最小化输出y2,y3,y4),Matlab完整源码

基本介绍 1.HKELM混合核极限学习机NSGAII多目标优化算法&#xff0c;工艺参数优化、工程设计优化&#xff01;&#xff08;Matlab完整源码和数据&#xff09; 多目标优化是指在优化问题中同时考虑多个目标的优化过程。在多目标优化中&#xff0c;通常存在多个冲突的目标&#x…

【AI智能体】Dify 基于知识库搭建智能客服问答应用详解

目录 一、前言 二、Dify 介绍 2.1 Dify 核心特点 三、AI智能体构建智能客服系统介绍 3.1 基于AI智能体平台搭建智能客服系统流程 3.1.1 需求分析与场景设计 3.1.2 选择合适的AI智能体平台 3.1.3 工作流编排与调试 3.1.4 系统集成与发布 3.2 使用AI智能体构建智能客服系…

事务~~~

1、四大特性&#xff1a;A 原子性&#xff1a;对数据的一组操作&#xff0c;要么执行成功&#xff0c;要么不执行C 一致性&#xff1a;事务前后的状态要保持一致&#xff0c;可以理解为数据的一致性I 隔离性&#xff1a;多个事务之间是隔离的&#xff0c;互不影响D 持久性&…

【Linux编译】./build.sh: line 17: $‘\r‘: command not found

文章目录0.运行编译脚本遇到问题&#xff1a;方法 1&#xff1a;使用 dos2unix&#xff08;推荐&#xff09;1. 安装 dos2unix2. 递归转换整个目录方法 2&#xff1a;使用 sed&#xff08;无需安装额外工具&#xff09;方法 3&#xff1a;使用 tr&#xff08;仅单文件&#xff…

Weblogic历史漏洞利用

文章目录漏洞介绍WebLogic 漏洞概述历史漏洞利用弱口令CVE-2014-4210CVE-2018-2894CVE-2019-2725CVE-2020-14882漏洞介绍 Oracle WebLogic Server 是一个用于开发和部署企业级 Java 应用的服务器平台&#xff0c;但其历史上存在多个严重漏洞&#xff0c;尤其以远程代码执行&am…

[Rust 基础课程]使用 Cargo 创建 Hello World 项目

Cargo&#xff08;https://crates.io/&#xff09; 是 Rust 语言中最常用的构建工具和包管理工具&#xff0c;我们看看怎么通过 Cargo 创建一个 Hello World 项目并运行。 :::warning 通过官方的 Rust 安装方式安装 Rust&#xff0c;Cargo 是同时默认安装好的了 ::: 首先&am…

C语言 --- 函数递归

函数递归一、什么是函数递归二、函数递归的要点三、示例1.计算n的阶乘2.提取一个任意正整数的所有位数&#xff0c;按顺序排列3.获取第n个斐波那契数&#xff0c;最开始的两个数是1&#xff0c;1四、总结一、什么是函数递归 函数递归是一种解决问题的思想&#xff0c;是将一个…

GitHub 趋势日报 (2025年07月14日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图1916claude-code795the-book-of-secret-knowledge728free-for-dev547markitdown367…

PyTorch中张量(TensorFlow)操作方法和属性汇总详解和代码示例

1、张量的操作汇总 下面是 PyTorch 中常见的 张量操作方法汇总&#xff0c;包括 创建、索引、变换、数学运算、广播机制、维度操作 等内容&#xff0c;并附上详解和代码示例&#xff0c;便于系统学习与实战参考。一、张量创建&#xff08;torch.tensor 等&#xff09; import t…

统一日志格式规范与 Filebeat+Logstash 实践落地

背景 在多部门、多技术栈并存的企业环境中&#xff0c;日志收集与分析是保障系统稳定运行的核心能力之一。然而&#xff0c;不同开发团队采用各异的日志打印方式&#xff0c;导致日志数据结构混乱&#xff0c;严重影响后续的收集、存储、检索与告警效率。 比如我们大部门就有多…

【鸿蒙HarmonyOS】鸿蒙app开发入门到实战教程(三):实现一个音乐列表的页面

鸿蒙里面&#xff0c;实现一个音乐播放的列表,模拟数组的数据展示 实现效果代码实现 准备数据 songs:SongItemTypes[] [{img:https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg,name:直到世界的尽头,author:WANDS},{img:https://yjy-teach-oss.oss-cn…

2025年渗透测试面试题总结-2025年HW(护网面试) 47(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2025年HW(护网面试) 47 1. UDF提权 2. 命令执行与代码执行的区别 3. 文件包含利用姿势 4. 漏洞复现流程 …

iPhone 数据擦除软件评测(最新且全面)

当您准备出售、捐赠或回收 iPhone 时&#xff0c;仅仅恢复出厂设置并不足以保证您的个人数据彻底消失。专业的 iPhone 数据擦除软件采用先进的技术&#xff0c;确保您的敏感信息永久无法恢复。本文回顾了十种流行的 iPhone 数据擦除工具&#xff0c;详细介绍了它们的功能、优点…

Qt 将触摸事件转换为鼠标事件(Qt4和Qt5及以上版本)

在Qt中&#xff0c;触摸事件&#xff08;QTouchEvent&#xff09;和鼠标事件&#xff08;QMouseEvent&#xff09;是两种不同的输入事件类型。通常情况下&#xff0c;触摸事件不会自动转换为鼠标事件&#xff0c;因为它们代表的是不同的输入设备&#xff08;触摸屏 vs 鼠标&…