目录
152. 乘积最大子数组
问题描述
给你一个整数数组 nums
,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
提示:
1 <= nums.length <= 2 * 10^4
-10 <= nums[i] <= 10
nums
的任何前缀或后缀的乘积都 保证 是一个 32-位 整数
leetcode53的简单变体?
跟昨天的【53.最大子数组和】如出一辙,只不过由和改成了积。对leecode53题解稍作修改可得。
假设数组的长度是 n,下标从 0 到 n-1。
我们用代表以第 i 个数结尾的「连续子数组的最大积」,那么很显然我们要求的答案就是:
因此我们只需要求出每个位置的 ,然后返回 所得f 数组中的最大值即可。那么我们如何求
呢?考虑
单独成为一段还是加入
对应的那一段,这取决于
和
的大小,我们希望获得一个比较大的,于是可以写出这样的动态规划转移方程:
考虑到 只和
相关,于是我们可以只用一个变量 来维护对于当前
的
的值是多少,从而让空间复杂度降低到 O(1),这有点类似「滚动数组」的思想。
但是事情真的这么简单吗?其实不然,因为乘法有个负负得正的效应。比如说,假设输入为[-2,-3,-4,-5],按照以上规则,会得到。在
的计算中,链条断了。如何解决这一问题?
思路与算法
对于每个,求以其结尾的最大连续子数组积和最小连续子数组积。每次都更新两个值,这样就可以把上面所说的那中问题cover进去。
代码
class Solution:
def maxProduct(self, nums: List[int]) -> int:
maxprod = minprod = nums[0]
prevmax = prevmin = nums[0]
for i in range(1,len(nums)):
curmax = max(prevmax*nums[i], prevmin*nums[i], nums[i])
curmin = min(prevmax*nums[i], prevmin*nums[i], nums[i])
maxprod = max(curmax, maxprod)
minprod = min(curmin, minprod)
prevmax,prevmin = curmax,curmin
return maxprod
执行用时:60 ms, 在所有 Python3 提交中击败了40.03%的用户
内存消耗:15.7 MB, 在所有 Python3 提交中击败了51.14%的用户
1567. 乘积为正数的最长子数组长度
问题描述
给你一个整数数组 nums
,请你求出乘积为正数的最长子数组的长度。
一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。
请你返回乘积为正数的最长子数组长度。
示例 1:
输入:nums = [1,-2,-3,4] 输出:4 解释:数组本身乘积就是正数,值为 24 。
示例 2:
输入:nums = [0,1,-2,-3,-4] 输出:3 解释:最长乘积为正数的子数组为 [1,-2,-3] ,乘积为 6 。 注意,我们不能把 0 也包括到子数组中,因为这样乘积为 0 ,不是正数。
示例 3:
输入:nums = [-1,-2,-3,0,1] 输出:2 解释:乘积为正数的最长子数组是 [-1,-2] 或者 [-2,-3] 。
提示:
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
思路与算法
同样要考虑乘法的负负得正的效应。借用上一题的思路。
假设 数组的长度是 n,下标从 0 到 n-1。
我们用代表以第 i 个数结尾的「乘积为正数的最长子数组的长度」,以
代表以第 i 个数结尾的「乘积为负数的最长子数组的长度」,那么很显然我们要求的答案就是:
因此我们只需要求出每个位置的 ,然后返回 所得f 数组中的最大值即可。那么我们如何求
呢?当然同时我们还要维护
。 可以得到如下转移方程。
如果大于0,那么
如果小于0,那么
如果 等于0,那么
考虑到,
只和
、
相关,于是我们可以只用两个来维护历史从而让空间复杂度降低到 O(1),这有点类似「滚动数组」的思想。
但是以上转移方程有漏洞。当nums[i-1]=0时,虽然在上一步已经将f(i-1)和g(i-1)都置为0了。但是下一个数比如说为正数时,显然g(i)应该还保持为0,而不是变为1。修正以上转移方程如下:
如果大于0,那么:
如果小于0,那么:
如果 等于0,那么
代码实现
class Solution:
def getMaxLen(self, nums: List[int]) -> int:
fmax = fprev = int(nums[0] > 0)
gmax = gprev = int(nums[0] < 0)
for i in range(1,len(nums)):
if nums[i]>0:
fcur = fprev+1
gcur = gprev+1 if gprev>0 else 0
elif nums[i]<0:
fcur = gprev+1 if gprev>0 else 0
gcur = fprev+1
else:
fcur = gcur = 0
fmax = max(fmax,fcur)
gmax = max(gmax,gcur)
fprev,gprev = fcur,gcur
return fmax
执行用时:216 ms, 在所有 Python3 提交中击败了25.16%的用户
内存消耗:26.2 MB, 在所有 Python3 提交中击败了66.23%的用户