欢迎访问LeetCode题解合集
题目描述
给你一个整数数组 nums
,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
题解:
动态规划。
设 f[i]
表示以 nums[i]
结尾, nums[0...i]
中最大子数组乘积,转移方程:f[i] = max{f[i - 1] * nums[i], nums[i]}
如果这题是 最大连续子数组和
问题,这个方程没问题,但是这题是 乘积
,乘积意味着可以 负负得正
,可能得到更大的结果。
假设 nums = {5, 6, -3, 4, -3}
,按上述转移得到的 f = {5, 30, -3, 4, -3}
,最大结果为 30
,而实际结果为所有元素的乘积,出现这种情况就是因为我们少考虑了 负数
的情况。
所以,我们需要维护一个 fmin[i]
,表示以 nums[i]
结尾,nums[0...i]
中最小子数组乘积,设前面的 f[i]
重新记为 fmax[i]
,那么转移方程为:
fmax[i] = max{ fmax[i - 1] * nums[i], fmin[i - 1] * nums[i], nums[i] }
fmin[i] = min{ fmax[i - 1] * nums[i], fmin[i - 1] * nums[i], nums[i] }
时间复杂度:$O(n)$
额外空间复杂度:$O(n)$
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size();
if ( !n ) return 0;
int ret = nums[0];
vector<int> fmin(n), fmax(n);
fmin[0] = fmax[0] = nums[0];
for ( int i = 1; i < n; ++i ) {
fmax[i] = max( {fmax[i - 1] * nums[i], fmin[i - 1] * nums[i], nums[i]} );
fmin[i] = min( {fmax[i - 1] * nums[i], fmin[i - 1] * nums[i], nums[i]} );
ret = max( ret, fmax[i] );
}
return ret;
}
};
/*
时间:4ms,击败:92.26%
内存:11.7MB,击败:58.58%
*/
可以发现,f
只跟 f[i - 1]
有关,使用滚动数组优化:
class Solution {
public:
int maxProduct(vector<int>& nums) {
if ( !nums.size() ) return 0;
int ret = nums[0];
int fmax = nums[0], fmin = nums[0];
for ( int i = 1, pre; i < nums.size(); ++i ) {
pre = fmax;
fmax = max( {pre * nums[i], fmin * nums[i], nums[i]} );
fmin = min( {pre * nums[i], fmin * nums[i], nums[i]} );
ret = max( {ret, fmax} );
}
return ret;
}
};
/*
时间:4ms,击败:92.26%
内存:11.3MB,击败:98.81%
*/