Find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example 1:
given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.
没有给出.
这道题跟以前做的一道求最大连续子序列和的问题类似,不同的是这道题求的是最大积。很自然能够想到用动态规划去解,设置一个数组dp,dp[i]表示以nums[i]结尾的子序列的最大积,初始状态:
dp[0] = nums[0];
状态转移方程就是:
dp[i] = max(dp[i - 1] * nums[i], nums[i])
这样就OK啦!
好吧,开玩笑的,如果真的是这样做,就完全掉入了这道题的陷阱里。求积里有个很重要的性质:负负得正。因此,如果有仔细观察题目的例子,会发现序列中会有负数的!所以,仅是记录最大值是不够的,比如序列[2, -1, -3],正确答案是 −1×−3=3 ,然而用上面那个解法会得到2,原因就在于中间计算时可能会产生负值,但我们不能判断当前的负值会不会与后面的负值相乘得到更大的结果。
正确的解法应该是同时记录最大积和最小积,dp[i][0]表示以nums[i]结尾的子序列的最小积,dp[i][1]表示以nums[i]结尾的子序列的最大积。
初始状态:
dp[0][0] = nums[0];
dp[0][1] = nums[0];
由于可能存在负数,所以有三个数参与判断,状态转移方程:
dp[i][0] = min( min(dp[i - 1][0] * nums[i], dp[i - 1][1] * nums[i]), nums[i])
dp[i][1] = max( max(dp[i - 1][0] * nums[i], dp[i - 1][1] * nums[i]), nums[i])
可以在用一个变量result记录结果,每次计算出最大积时就更新一下result,最后返回result就行,见下面我的代码1,时间复杂度是 O(n) ,空间复杂度是 O(n) 。
通过状态转移方程可以看出计算dp[i][]时只需要用到dp[i - 1][],与dp[i - 2][]及前面的结果没有关系,因此空间复杂度可以进一步优化,只用两个变量minPro和maxPro存储前一个位置的最大积和最小积。
初始状态:
minPro = nums[0];
maxPro = nums[0];
状态转移方程:
minPro = min( min(minPro * nums[i], maxPro * nums[i]), nums[i])
maxPro = max( max(minPro * nums[i], maxPro * nums[i]), nums[i])
另外状态转移方程中重复做了两次乘法,所以可以用变量先算出结果再进行判断,见下文中我的代码2,时间复杂度是 O(n) ,空间复杂度是 O(1) 。
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size(), result = nums[0];
// dp[i][0] min, dp[i][1] max
vector<vector<int>> dp(n, vector<int>(2, nums[0]));
dp[0][0] = nums[0];
dp[0][1] = nums[0];
for (int i = 1; i < n; i++) {
dp[i][0] = min(min(dp[i - 1][0] * nums[i], dp[i - 1][1] * nums[i]), nums[i]);
dp[i][1] = max(max(dp[i - 1][0] * nums[i], dp[i - 1][1] * nums[i]), nums[i]);
if (dp[i][1] > result) {
result = dp[i][1];
}
}
return result;
}
};
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size(), result = nums[0];
int minPro = nums[0], maxPro = nums[0];
int temp0 = 0, temp1 = 0;
for (int i = 1; i < n; i++) {
temp0 = minPro * nums[i];
temp1 = maxPro * nums[i];
minPro = min(min(temp0, temp1), nums[i]);
maxPro = max(max(temp0, temp1), nums[i]);
result = max(maxPro, result);
}
return result;
}
};
这道题是一道比较经典的子序列变种体,关键是弄清楚结果的产生情况,既可能是由正数相乘得到,也有可能是由负数得到,所以必须存储最大积和最小积。
动归的题目通过之后可以想想能不能优化,一般是针对空间进行优化。因为我的能力比较差,所以一开始都是开最大的空间((⊙﹏⊙)b)。通过想想优化,能够更好地体会状态转移的过程,所以得养成这个习惯,勤能补拙,继续加油啦~