1567. 乘积为正数的最长子数组长度
考点
- 状态机DP
一、题目简述
给定一个整数数组
nums,求乘积为正数的最长连续子数组长度。
需要注意三种数值类型对乘积符号的影响:
- 正数:不改变符号
- 负数:翻转符号
- 0:直接中断子数组
二、状态定义(State Definition)
定义二维 DP: \[ f[i][0] = \text{以第 } i \text{ 个元素结尾,乘积为正的最长连续子数组长度} \] 这里的关键点是:
- 一定是“以 \(i\) 结尾”
- 必须包含
nums[i] - 所有不以
i结尾的子数组,由最终答案统一取最大值
三、初始值(Initialization)
由于数组从 i = 1 开始计算: \[
f[0][0] = f[0][1] = 0
\] 表示在没有任何元素时,不存在合法子数组。
当遇到:
- 正数 → 可单独形成正积子数组
- 负数 → 可单独形成负积子数组
- 0 → 全部清零(子数组被截断)
这些都在状态转移时隐式完成初始化。
四、状态转移方程(Transition Equations)
设当前元素: \[ x = nums[i] \]
✅ 情况一:当前元素为正数 \(x > 0\)
- 正 × 正 = 正
- 负 × 正 = 负
状态转移为: \[ f[i][0] = \max(1, f[i-1][0] + 1) \]
✅ 情况二:当前元素为负数 \(x < 0\)
- 正 × 负 = 负
- 负 × 负 = 正
状态转移为: \[ f[i][1] = \max(1, f[i-1][0] + 1) \]
✅ 情况三:当前元素为 0
由于 0 会直接使乘积变为 0,不可能为正或负,因此: \[ f[i][0] = 0,\quad f[i][1] = 0 \] 相当于子数组在此被强制截断。
五、最终答案(Result Extraction)
由于最长子数组不一定以最后一个元素结尾,因此答案应为: \[ \text{ans} = \max_{1 \le i \le n} f[i][0] \] 即:
1 | int res = 0; |
六、完整 AC 代码
1 | class Solution { |
当然也可以滚动数组:
1 | class Solution { |
七、补充说明(用于讲解时强调)
- 本题本质是符号状态机 DP
- 负数的作用是:正负互换
- 0 的作用是:状态清零,强制断开
f[i][0]才是合法答案来源,因为题目要求乘积为正