题目链接
给你一个整数数组 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
这意味着有效子序列只有以下几种模式:
全奇数序列:[奇, 奇, 奇, ...]
奇 + 奇 = 偶
全偶数序列:[偶, 偶, 偶, ...]
偶 + 偶 = 偶
奇偶交替序列:[奇, 偶, 奇, 偶, ...]
奇 + 偶 = 奇
偶奇交替序列:[偶, 奇, 偶, 奇, ...]
偶 + 奇 = 奇
我们需要分别计算这四种模式的最大长度,然后取最大值:
dp[0]
:当前以偶数结尾的最长交替子序列长度dp[1]
:当前以奇数结尾的最长交替子序列长度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;
}
}
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;
}
};
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]
[1,2,1,2,1,2]
,长度为 6最大长度为 6。
这道题考查的核心知识点:
解题要点:
这是一道很好的逻辑思维和数学分析题目,重点在于理解题意和找到所有可能的有效模式。