【Leetcode】3201. 找出有效子序列的最大长度 I

文章目录

  • 题目
    • 题目描述
    • 示例
    • 提示
  • 思路分析
    • 核心观察
    • 有效子序列的四种模式
    • 算法思路
  • 代码实现
    • Java版本
    • C++版本
    • Python版本
  • 优化版本
  • 复杂度分析
    • 时间复杂度
    • 空间复杂度
  • 示例验证
  • 总结

题目

题目链接

题目描述

给你一个整数数组 nums

nums 的子序列 sub 的长度为 x,如果其满足以下条件,则称其为有效子序列

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

返回 nums最长的有效子序列的长度。

一个子序列指的是从原数组中删除一些元素(也可以不删除任何元素),剩余元素保持原来顺序组成的新数组。

示例

示例 1:

  • 输入:nums = [1,2,3,4]
  • 输出:4
  • 解释:最长的有效子序列是 [1, 2, 3, 4]

示例 2:

  • 输入:nums = [1,2,1,1,2,1,2]
  • 输出:6
  • 解释:最长的有效子序列是 [1, 2, 1, 2, 1, 2]

示例 3:

  • 输入:nums = [1,3]
  • 输出:2
  • 解释:最长的有效子序列是 [1, 3]

提示

  • 2 <= nums.length <= 2 * 10^5
  • 1 <= nums[i] <= 10^7

思路分析

核心观察

要理解这道题,我们需要分析什么样的子序列是有效的。

对于有效子序列,要求相邻元素之和的奇偶性相同:

  • (sub[0] + sub[1]) % 2 == (sub[1] + sub[2]) % 2

a, b, c 是子序列中连续的三个元素,如果 (a + b) % 2 == (b + c) % 2,那么:

  • 如果 a + b 是偶数,那么 b + c 也必须是偶数
  • 如果 a + b 是奇数,那么 b + c 也必须是奇数

通过数学分析可以得出:a % 2 == c % 2

这意味着有效子序列只有以下几种模式:

有效子序列的四种模式

  1. 全奇数序列[奇, 奇, 奇, ...]

    • 相邻和都是偶数:奇 + 奇 = 偶
  2. 全偶数序列[偶, 偶, 偶, ...]

    • 相邻和都是偶数:偶 + 偶 = 偶
  3. 奇偶交替序列[奇, 偶, 奇, 偶, ...]

    • 相邻和都是奇数:奇 + 偶 = 奇
  4. 偶奇交替序列[偶, 奇, 偶, 奇, ...]

    • 相邻和都是奇数:偶 + 奇 = 奇

算法思路

我们需要分别计算这四种模式的最大长度,然后取最大值:

  1. 模式1和2:直接统计奇数和偶数的个数
  2. 模式3和4:使用动态规划思想
    • dp[0]:当前以偶数结尾的最长交替子序列长度
    • dp[1]:当前以奇数结尾的最长交替子序列长度

代码实现

Java版本

class Solution {
    public int maximumLength(int[] nums) {
        int oddCount = 0;   // 奇数个数
        int evenCount = 0;  // 偶数个数
        
        // 统计奇偶数个数
        for (int num : nums) {
            if (num % 2 == 1) {
                oddCount++;
            } else {
                evenCount++;
            }
        }
        
        // 模式1: 全奇数,模式2: 全偶数
        int maxLen = Math.max(oddCount, evenCount);
        
        // 模式3: 奇偶交替 (以奇数开头)
        int oddEvenLen = 0;
        int expectOdd = 1; // 1表示期望奇数,0表示期望偶数
        for (int num : nums) {
            if (num % 2 == expectOdd) {
                oddEvenLen++;
                expectOdd = 1 - expectOdd; // 切换期望
            }
        }
        maxLen = Math.max(maxLen, oddEvenLen);
        
        // 模式4: 偶奇交替 (以偶数开头)  
        int evenOddLen = 0;
        expectOdd = 0; // 开始期望偶数
        for (int num : nums) {
            if (num % 2 == expectOdd) {
                evenOddLen++;
                expectOdd = 1 - expectOdd; // 切换期望
            }
        }
        maxLen = Math.max(maxLen, evenOddLen);
        
        return maxLen;
    }
}

C++版本

class Solution {
public:
    int maximumLength(vector<int>& nums) {
        int oddCount = 0, evenCount = 0;
        
        // 统计奇偶数个数
        for (int num : nums) {
            if (num % 2 == 1) oddCount++;
            else evenCount++;
        }
        
        int maxLen = max(oddCount, evenCount);
        
        // 奇偶交替模式
        int oddEvenLen = 0;
        int expectOdd = 1;
        for (int num : nums) {
            if (num % 2 == expectOdd) {
                oddEvenLen++;
                expectOdd = 1 - expectOdd;
            }
        }
        maxLen = max(maxLen, oddEvenLen);
        
        // 偶奇交替模式
        int evenOddLen = 0;
        expectOdd = 0;
        for (int num : nums) {
            if (num % 2 == expectOdd) {
                evenOddLen++;
                expectOdd = 1 - expectOdd;
            }
        }
        maxLen = max(maxLen, evenOddLen);
        
        return maxLen;
    }
};

Python版本

class Solution:
    def maximumLength(self, nums: List[int]) -> int:
        odd_count = sum(1 for num in nums if num % 2 == 1)
        even_count = len(nums) - odd_count
        
        max_len = max(odd_count, even_count)
        
        # 奇偶交替模式
        odd_even_len = 0
        expect_odd = 1
        for num in nums:
            if num % 2 == expect_odd:
                odd_even_len += 1
                expect_odd = 1 - expect_odd
        max_len = max(max_len, odd_even_len)
        
        # 偶奇交替模式
        even_odd_len = 0
        expect_odd = 0
        for num in nums:
            if num % 2 == expect_odd:
                even_odd_len += 1
                expect_odd = 1 - expect_odd
        max_len = max(max_len, even_odd_len)
        
        return max_len

优化版本

我们可以用更简洁的动态规划方法:

class Solution {
    public int maximumLength(int[] nums) {
        int oddCount = 0, evenCount = 0;
        int[] dp = new int[2]; // dp[0]: 以偶数结尾, dp[1]: 以奇数结尾
        
        for (int num : nums) {
            int parity = num % 2;
            if (parity == 1) oddCount++;
            else evenCount++;
            
            dp[parity] = dp[1 - parity] + 1;
        }
        
        return Math.max(Math.max(oddCount, evenCount), Math.max(dp[0], dp[1]));
    }
}

复杂度分析

时间复杂度

O(n),其中 n 是数组的长度。我们需要遍历数组常数次。

空间复杂度

O(1),只使用了常数额外空间。

示例验证

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

  • 奇数:[1,3],长度为 2
  • 偶数:[2,4],长度为 2
  • 奇偶交替:[1,2,3,4],长度为 4
  • 偶奇交替:[2,3,4] 或从2开始,长度为 3

最大长度为 4。

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

  • 奇数个数:4,偶数个数:3
  • 奇偶交替:[1,2,1,2,1,2],长度为 6
  • 偶奇交替:从偶数开始的最长为 3

最大长度为 6。

总结

这道题考查的核心知识点:

  1. 数学分析:理解奇偶性运算规律
  2. 贪心思想:对于交替模式,贪心地选择满足条件的元素
  3. 动态规划:用状态表示当前模式下的最优解

解题要点:

  • 准确分析有效子序列的数学性质
  • 识别出四种可能的模式
  • 对每种模式分别求最优解
  • 取全局最优

这是一道很好的逻辑思维和数学分析题目,重点在于理解题意和找到所有可能的有效模式。

你可能感兴趣的:(练习题(记录做题想法),leetcode,算法,职场和发展)