1567. 乘积为正数的最长子数组长度 ●●
描述
给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。
一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。
请你返回乘积为正数的最长子数组长度。
示例
输入:nums = [0,1,-2,-3,-4]
输出:3
解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。
注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。
题解
1. 动态规划
-
pos[i]
表示下标 i 为结尾时,乘积为正数的最长子数组长度;
neg[i]
表示下标 i 为结尾时,乘积为负数的最长子数组长度。 -
根据 nums[0] 初始化dp数组,
当 nums[0] > 0 时 pos[0] = 1,当 nums[0] < 0 时 neg[0] = 1。 -
往后遍历数组 nums:
-
nums[i] > 0,不改变乘积符号,
pos[i] = pos[i-1] + 1;
neg[i] = neg[i-1] == 0? 0 : neg[i-1] + 1;
当 neg[i−1]=0 时,neg[i] 本身无法形成一个乘积为正数的子数组,所以要特殊判断,值为0。 -
nums[i] < 0,改变乘积符号,
neg[i] = pos[i-1] + 1;
pos[i] = neg[i-1] == 0? 0 : neg[i-1] + 1;
当 pos[i−1]=0 时,pos[i] 本身无法形成一个乘积为正数的子数组,所以要特殊判断,值为0。 -
nums[i] = 0,都为 0,
pos[i] = 0;
neg[i] = 0;
-
- 时间复杂度:O(n),其中 n 是数组的长度。遍历数组一次,在遍历过程中维护最长子数组长度,对于数组中的每个元素的时间复杂度都是 O(1),因此总时间复杂度是 O(n)。
- 空间复杂度:O(n)。
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n = nums.size();
vector<int> pos(n, 0); // dp数组
vector<int> neg(n, 0);
int maxPoslen = 0;
if(nums[0] > 0){ // 初始化
pos[0] = 1;
maxPoslen = 1;
}else if(nums[0] < 0) {
neg[0] = 1;
}
for(int i = 1; i < n; ++i){
if(nums[i] > 0){
pos[i] = pos[i-1] + 1;
neg[i] = neg[i-1] == 0? 0 : neg[i-1] + 1;
}else if(nums[i] < 0){
pos[i] = neg[i-1] == 0? 0 : neg[i-1] + 1;
neg[i] = pos[i-1] + 1;
}else{
pos[i] = 0;
neg[i] = 0;
}
maxPoslen = max(maxPoslen, pos[i]);
}
return maxPoslen;
}
};
- 一维滚动数组优化空间复杂度 O ( 1 ) O(1) O(1)
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n = nums.size();
int maxPoslen = 0, pos = 0, neg = 0;
if(nums[0] > 0){
pos = 1;
maxPoslen = 1;
}else if(nums[0] < 0) {
neg = 1;
}
for(int i = 1; i < n; ++i){
int prePos = pos, preNeg = neg;
if(nums[i] > 0){
pos = prePos + 1;
neg = preNeg == 0? 0 : preNeg + 1;
}else if(nums[i] < 0){
pos = preNeg == 0? 0 : preNeg + 1;
neg = prePos + 1;
}else{
pos = 0;
neg = 0;
}
maxPoslen = max(maxPoslen, pos);
}
return maxPoslen;
}
};
2. 双指针 滑动窗口
0与任何数的乘积都是0,因此可以将数组看成被0分割的子数组,在各个子数组中查找乘积为正数的长度。
1、right指针向右移动,cnt 累计 left 至 right 指针之间的负数个数,如果负数个数为偶数,则记录最大子数组长度,直至 right 遇到 0 或末尾。
2、向右移动 left 指针,cnt 减去负数的个数,如果负数个数为偶数,则记录最大子数组长度,直至 left 追上 right 。
重复以上过程直至left指针移动到末尾。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n = nums.size();
int l = 0, r = 0;
int neg_cnt = 0, ans = 0;
while(l < n){
while(r < n && nums[r] != 0){ // r 右移
if(nums[r] < 0) ++neg_cnt; // 更新负数个数
if(neg_cnt % 2 == 0) ans = max(ans, r-l+1);
++r;
}
while(l <= r-1){ // l 移向 r
if(nums[l] < 0) --neg_cnt; // 更新负数个数
++l;
if(neg_cnt % 2 == 0) ans = max(ans, r-l);
}
while(r < n && nums[r] == 0){ // 跳过 0
++r;
}
l = r; // 新的子数组
neg_cnt = 0;
}
return ans;
}
};