LeetCode 152. Maximum Product Subarray (最大乘積子陣列)
Given an integer array nums
, find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example 1:
Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Example 2:
Input: [-2,0,-1] Output: 0 Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
Reference Answer
思路分析
這個求最大子陣列乘積問題是由最大子陣列之和問題演變而來,但是卻比求最大子陣列之和要複雜,因為在求和的時候,遇到0,不會改變最大值,遇到負數,也只是會減小最大值而已。而在求最大子陣列乘積的問題中,遇到0會使整個乘積為0,而遇到負數,則會使最大乘積變成最小乘積,正因為有負數和0的存在,使問題變得複雜了不少。。
比如,我們現在有一個數組[2, 3, -2, 4],我們可以很容易的找出所有的連續子陣列,[2], [3], [-2], [4], [2, 3], [3, -2], [-2, 4], [2, 3, -2], [3, -2, 4], [2, 3, -2, 4], 然後可以很輕鬆的算出最大的子陣列乘積為6,來自子陣列[2, 3].
那麼我們如何寫程式碼來實現自動找出最大子陣列乘積呢,我最先想到的方比較簡單粗暴,就是找出所有的子陣列,然後算出每一個子陣列的乘積,然後比較找出最大的一個,需要兩個for迴圈,第一個for遍歷整個陣列,第二個for遍歷含有當前數字的子陣列,就是按以下順序找出子陣列: [2], [2, 3], [2, 3, -2], [2, 3, -2, 4], [3], [3, -2], [3, -2, 4], [-2], [-2, 4], [4], 我在本地測試的一些陣列全部通過,於是興高采烈的拿到OJ上測試,結果喪心病狂的OJ用一個有15000個數字的陣列來測試,然後說我程式的執行時間超過了要求值,我一看我的程式碼,果然如此,時間複雜度O(n2), 得想辦法只用一次迴圈搞定。我想來想去想不出好方法,於是到網上搜各位大神的解決方法。其實這道題最直接的方法就是用DP來做,而且要用兩個dp陣列,其中f[i]表示子陣列[0, i]範圍內的最大子陣列乘積,g[i]表示子陣列[0, i]範圍內的最小子陣列乘積,初始化時f[0]和g[0]都初始化為nums[0],其餘都初始化為0。那麼從陣列的第二個數字開始遍歷,那麼此時的最大值和最小值只會在這三個數字之間產生,即f[i-1]*nums[i],g[i-1]*nums[i],和nums[i]。所以我們用三者中的最大值來更新f[i],用最小值來更新g[i],然後用f[i]來更新結果res即可。
Code
class Solution:
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 1:
return nums[0]
res = max_temp = min_temp = nums[0]
for count in nums[1:]:
pre_max, pre_min = max_temp, min_temp
max_temp = max(pre_max*count, count, pre_min*count)
min_temp = min(pre_max*count, count, pre_min*count)
res = max(res, max_temp)
return res
C++ Version
class Solution {
public:
int maxProduct(vector<int>& nums) {
if (nums.size() == 1){
return nums[0];
}
int res = nums[0], min_temp = nums[0], max_temp = nums[0];
for (int i=1; i<nums.size(); ++i){
int pre_max = max_temp;
int pre_min = min_temp;
max_temp = max(max(pre_max*nums[i], nums[i]), pre_min*nums[i]);
min_temp = min(min(pre_max*nums[i], nums[i]), pre_min*nums[i]);
res = max(max_temp, res);
}
return res;
}
};
Note
- 這種應多複雜情況採用採用多個dp陣列記錄狀態的做法很值得學習,後面很多DP問題都是需要借鑑這種多個dp陣列記錄狀態應對複雜場景約束的動態規劃問題!