题目如下:
Find the contiguous subarray within an array (containing at least one number) which has the largest product.
For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.
分析思路:
一开始看这题目的样子以为要用动态规划,但是完全不好写动态规划的方程。其实,题目中的输入可以这么来看,
如果输入数组中一个0都没有,那么就把所有的输入全部相乘得到一个结果,如果这个结果为>0,必然是largest product,如果结果<0,那么就从输入数组的左边开始向右扫描,直到找到第1个负数为主,那么此时去掉左边的subarray(从输入数组的第1个开始,到第1个负数为止的子数组。例如,[-2, 2, 3, -3]的左subarray为[-2], [2, -2, 3, -3]的左subarray为[2, -2])后,去掉左边的subarray后剩下数的乘积必然为正数。同样的过程按照从右到左的顺序对右边的数组走一遍,得到去掉右边subarray后剩下数的乘积,也必然为正数。比较两个正数,较大者为所求。把上面的过程放入辅助函数subsetMax中。
如果输入数组中有0,就把0作为分界线,得出一些子数组,对每个子数组做上面的事情。在每个子数组得到的最大乘积中选择最大的。
比较特殊的情况是 当输入数组为[-1],此时需要在subsetMax中特殊处理一下。
这道题目也是个非常具有面试题目感觉的题目,没有任何算法纯代码,但是代码很有区分度。
代码如下:
class Solution { private: int subsetMax(int sub[], int n) { if (n == 1) return sub[0]; int subsetProduct = 1; int posStart = -1; int posEnd = -1; for (int i = 0; i < n ; ++i) subsetProduct *= sub[i]; if (subsetProduct > 0) return subsetProduct; int leftSubsetProduct = 1; int rightSubsetProduct = 1; for (posStart = 0; posStart < n ; ++posStart) if (sub[posStart] < 0) break; for (int i = posStart + 1; i < n; ++i) leftSubsetProduct *= sub[i]; for (posEnd= n-1; posEnd >= 0 ; --posEnd) if (sub[posEnd] < 0) break; for (int i = posEnd - 1; i >= 0; --i) rightSubsetProduct *= sub[i]; return leftSubsetProduct > rightSubsetProduct ? leftSubsetProduct:rightSubsetProduct; } public: int maxProduct(int A[], int n) { int arrayMax = INT_MIN; int subStart = 0; int subEnd = 0; int curMax = INT_MIN; bool hasZero = false; while (subStart < n) { // 简化了while (subStart < n && subEnd < n) 因为while{}内部的最后一步是subStart = subEnd + 1; while (subStart < n && A[subStart]==0) { hasZero = true; subStart++; } subEnd = subStart; while (subEnd < n && A[subEnd]!=0) subEnd++; if (subEnd < n) hasZero = true; if (subStart < subEnd) { curMax = subsetMax(A + subStart, subEnd - subStart); if (curMax > arrayMax) arrayMax = curMax; } subStart = subEnd + 1; } if (hasZero && arrayMax < 0) return 0; else return arrayMax; } };
update 2014-11-23
更简洁的思路是动态规划。解答思路来自官网的答案解析。
Let us denote that:
f(k) = Largest product subarray, from index 0 up to k. Similarly,
g(k) = Smallest product subarray, from index 0 up to k. Then,
f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] )
g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] )
There we have a dynamic programming formula. Using two arrays of size n, we could deduce the final answer as f(n-1).
class Solution { public: int maxProduct(int A[], int n) { if (n == 0) return 0; if (n == 1) return A[0]; int result = A[0]; //NOTE: 不能初始化为resutl = INT_MIN,因为已经用了A[0]作为基准了。 int min_product = A[0]; int max_product = A[0]; for (int i = 1; i < n; ++i) { int org_min = min_product; //NOTE: data dependency: 原始的min max值需要保留下来。 int org_max = max_product; //NOTE: data dependency: 原始的min max值需要保留下来。 max_product = ((org_max * A[i]) > (org_min * A[i]))?(org_max * A[i]):(org_min * A[i]); max_product = (max_product > A[i]) ? max_product:A[i]; min_product = ((org_max * A[i]) < (org_min * A[i]))?(org_max * A[i]):(org_min * A[i]); min_product = (min_product < A[i]) ? min_product:A[i]; if (max_product > result) result = max_product; //std::cout<<"i = "<<i<<", max="<<max_product<<", min="<< min_product<<" ,res="<<result<<std::endl; } return result; } };