🚩传送门:牛客题目
力扣题目

题目

给你一个整数数组 [NC]306. 乘积为正数的最长连续子数组 - 图1 ,请你求出乘积为正数的最长子数组的长度。

一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。

请你返回乘积为正数的最长子数组长度。

示例:

输入:nums = [0,1,-2,-3,-4] 输出:3 解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。 注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。

解题思路:动态规划

👉 定义:[NC]306. 乘积为正数的最长连续子数组 - 图2

  • 其中[NC]306. 乘积为正数的最长连续子数组 - 图3 表示以第 [NC]306. 乘积为正数的最长连续子数组 - 图4 个数做结尾,最长的正数序列长度
  • 其中[NC]306. 乘积为正数的最长连续子数组 - 图5 表示以第 [NC]306. 乘积为正数的最长连续子数组 - 图6 个数做结尾,最长的负数序列长度

那么我们不难发现,对于得到一个正负数序列,只有下面的方式
[NC]306. 乘积为正数的最长连续子数组 - 图7
[NC]306. 乘积为正数的最长连续子数组 - 图8

那么我们可以遍历数组的每个数字,对于每个数字分正负讨论

  • 当前数字[NC]306. 乘积为正数的最长连续子数组 - 图9正数

对于 正数序列 那么我们只需要知道[NC]306. 乘积为正数的最长连续子数组 - 图10的长度,[NC]306. 乘积为正数的最长连续子数组 - 图11在其基础上_+1_即可
对于 负数序列 那么我们只需要知道[NC]306. 乘积为正数的最长连续子数组 - 图12的长度,[NC]306. 乘积为正数的最长连续子数组 - 图13在其基础上_+1_即可

  • 当前数字[NC]306. 乘积为正数的最长连续子数组 - 图14负数

对于 负数序列 那么我们只需要知道[NC]306. 乘积为正数的最长连续子数组 - 图15的长度,[NC]306. 乘积为正数的最长连续子数组 - 图16在其基础上_+1_即可
对于 正数序列 那么我们只需要知道[NC]306. 乘积为正数的最长连续子数组 - 图17的长度,[NC]306. 乘积为正数的最长连续子数组 - 图18在其基础上_+1_即可

这样貌似可以了,真的吗?
如果当前数字[NC]306. 乘积为正数的最长连续子数组 - 图19正数,但是[NC]306. 乘积为正数的最长连续子数组 - 图20的长度为[NC]306. 乘积为正数的最长连续子数组 - 图21怎么办,[NC]306. 乘积为正数的最长连续子数组 - 图22无论如何都构建不出以第 [NC]306. 乘积为正数的最长连续子数组 - 图23 个数做结尾的负数序列,这时候只能[NC]306. 乘积为正数的最长连续子数组 - 图24
同理
如果当前数字[NC]306. 乘积为正数的最长连续子数组 - 图25负数,但是[NC]306. 乘积为正数的最长连续子数组 - 图26的长度为[NC]306. 乘积为正数的最长连续子数组 - 图27怎么办,[NC]306. 乘积为正数的最长连续子数组 - 图28无论如何都构建不出以第 [NC]306. 乘积为正数的最长连续子数组 - 图29 个数做结尾的正数序列,这时候只能[NC]306. 乘积为正数的最长连续子数组 - 图30

那么加上前序列是否为零就可以了吗?
如果当前数字[NC]306. 乘积为正数的最长连续子数组 - 图31零值,无论[NC]306. 乘积为正数的最长连续子数组 - 图32的长度是多少

  • [NC]306. 乘积为正数的最长连续子数组 - 图33无论如何都构建不出以第 [NC]306. 乘积为正数的最长连续子数组 - 图34 个数做结尾的负数序列,这时候只能[NC]306. 乘积为正数的最长连续子数组 - 图35
  • [NC]306. 乘积为正数的最长连续子数组 - 图36无论如何都构建不出以第 [NC]306. 乘积为正数的最长连续子数组 - 图37 个数做结尾的正数序列,这时候只能[NC]306. 乘积为正数的最长连续子数组 - 图38

复杂度分析

时间复杂度: [NC]306. 乘积为正数的最长连续子数组 - 图39 ,其中 [NC]306. 乘积为正数的最长连续子数组 - 图40 是数组的长度,只需要遍历数组一次。

空间复杂度:[NC]306. 乘积为正数的最长连续子数组 - 图41 ,即为存储 [NC]306. 乘积为正数的最长连续子数组 - 图42 所有状态使用的空间。

我的代码

  1. public class Solution {
  2. public int findLongestSubArray (ArrayList<Integer> nums) {
  3. int[][]dp=new int[nums.size()][2];
  4. if(nums.get(0)>0)dp[0][0]=1;
  5. else if(nums.get(0)<0)dp[0][1]=1;
  6. else ;
  7. int res=0;
  8. for(int i=1;i<nums.size();i++){
  9. if(nums.get(i)>0){
  10. // 正数=当前正数*前面正数 ,因此长度是在前面正数序列的长度上+1
  11. dp[i][0]=Math.max(dp[i-1][0]+1,dp[i][0]);
  12. // 负数=当前正数*前面负数,因此长度是在前面负数序列的长度上+1
  13. if(dp[i-1][1]==0){ // 特殊情况:当前正数,但上一个负数序列长度为0,无法构成负数序列
  14. dp[i][1]=0;
  15. }else dp[i][1]=Math.max(dp[i-1][1]+1,dp[i][1]);
  16. }else if(nums.get(i)<0){
  17. // 负数=当前负数*前面正数,因此长度是在前面正数序列的长度上+1
  18. dp[i][1]=Math.max(dp[i-1][0]+1,dp[i][1]);
  19. // 正数=当前负数*前面负数,因此长度是在前面负数序列的长度上+1
  20. if(dp[i-1][1]==0){ // 特殊情况:当前负数,但上一个正数序列长度为0,无法构成正数序列
  21. dp[i][0]=0;
  22. }else dp[i][0]=Math.max(dp[i-1][1]+1,dp[i][0]);
  23. }else{
  24. // 0 , 0乘以任何数都是0
  25. dp[i][0]=0;
  26. dp[i][1]=0;
  27. }
  28. res=Math.max(res,dp[i][0]);
  29. }
  30. return res;
  31. }
  32. }

我们不难发现[NC]306. 乘积为正数的最长连续子数组 - 图43的状态只和[NC]306. 乘积为正数的最长连续子数组 - 图44有关,因此我们可以使用dp_pre_0dp_pre_1来保存前者的状态

复杂度分析

时间复杂度: [NC]306. 乘积为正数的最长连续子数组 - 图45 ,其中 [NC]306. 乘积为正数的最长连续子数组 - 图46 是数组的长度,只需要遍历数组一次。

空间复杂度:[NC]306. 乘积为正数的最长连续子数组 - 图47 ,常数空间。

优化代码

  1. public class Solution {
  2. public int findLongestSubArray (ArrayList<Integer> nums) {
  3. int[][]dp=new int[nums.size()][2];
  4. int dp_pre_0=0; // 保存前项正数序列长度
  5. int dp_pre_1=0; // 保存前向负数序列长度
  6. int dp_cur_0=0; // 保存当前正数序列长度
  7. int dp_cur_1=0; // 保存当前负数序列长度
  8. if(nums.get(0)>0)dp_pre_0=1;
  9. else if(nums.get(0)<0)dp_pre_1=1;
  10. else ;
  11. int res=0;
  12. for(int i=1;i<nums.size();i++){
  13. if(nums.get(i)>0){
  14. // 正数序列=当前正数*前面正数序列,因此长度是在前面正数序列的长度上+1
  15. dp_cur_0=dp_pre_0+1;
  16. // 负数序列=当前正数*前面负数序列,因此长度是在前面负数序列的长度上+1
  17. if(dp_pre_1==0){ // 特殊情况:上一个负数序列长度为0,当前是正数,故无法构成负数序列
  18. dp_cur_1=0;
  19. }else dp_cur_1=dp_pre_1+1;
  20. }else if(nums.get(i)<0){
  21. // 负数序列=当前负数*前面正数序列,因此长度是在前面正数序列的长度上+1
  22. dp_cur_1=dp_pre_0+1;
  23. // 正数序列=当前负数*前面负数序列,因此长度是在前面负数序列的长度上+1
  24. if(dp_pre_1==0){ // 特殊情况:上一个正数序列长度为0,当前是负数,故无法构成负数序列
  25. dp_cur_0=0;
  26. }else dp_cur_0=dp_pre_1+1;
  27. }else{
  28. // 当前数为0 , 0乘以任何数都是0
  29. dp_cur_0=0;
  30. dp_cur_1=0;
  31. }
  32. res=Math.max(res,dp_cur_0);
  33. dp_pre_0=dp_cur_0;
  34. dp_pre_1=dp_cur_1;
  35. }
  36. return res;
  37. }
  38. }