给定一个数组,求其中乘积最大的子串的乘积。如数组[2,3,-2,4],最大乘积为2*3=6.
分析:最简单的方法是设置一个二维数组p[i][j]表示从 i 到 j 这个子串的乘积,然后求p中的最大值。但这种方法时间复杂度和空间复杂度都比较高。
计算子串的乘积,如果中间包含了0,那么整个子串的乘积就是0。因此,需要以0为分界点,把数组分成几个子串,再分别求最大乘积。计算子串的最大乘积的方法有以下几种方法:
方法一:因为数组中都是整数,所以子串越长,绝对值越大,因此可以先计算子串中负数的总数,如果总数为偶数,那么所有元素的乘积是最大的正数;如果负数的总数为奇数,使p1 = 从第一个到最后一个负数之前元素的乘积, p2 = 第一个负数后的元素开始到最后一个元素的乘积,p1和p2之间的最大值就是这个子串的最大值。
public int maxProduct(int[] nums) {
if(nums.length == 0)
return 0;
List<Integer> zeros = new LinkedList<Integer>();
zeros.add(nums.length);
zeros.add(-1);
for(int i = 0; i < nums.length; i++){ //倒序插入0的位置
if(nums[i] == 0)
zeros.add(1, i);
}
if(zeros.size() == 2)
return product(nums, 0, nums.length - 1); //没有0
List<Integer> products = new ArrayList<Integer>();
for(int i = 0; i < zeros.size() - 1; i++){ //各子串的最大乘积
products.add(product(nums, zeros.get(i + 1) + 1, zeros.get(i) - 1));
}
int max = Integer.MIN_VALUE;
for(int p : products)
if(p > max)
max = p;
return Math.max(max, 0);
}
public int product(int[] nums, int start, int end){
if(end < start) return 0;
if(end == start) return nums[start];
int count = 0;
int first = -1; //第一个负数的位置
int last = -1; //最后一个负数的位置
int max = nums[start];
for(int i = start; i <= end; i++){
if(nums[i] < 0){
count++;
if(first == -1) first = i;
last = i;
}
}
if(count % 2 == 0){ //偶数
for(int i = start + 1; i <= end; i++){
max *= nums[i];
}
return max;
}
else{ //奇数
int p1 = 1, p2 = 1;
for(int i = start; i < last; i++)
p1 *= nums[i];
for(int i = first + 1; i <= end; i++)
p2 *= nums[i];
return Math.max(p1, p2);
}
}
方法二:维持两个变量:最大值和最小值,如果乘以一个负数,最大值将会变成最小值,最小值变成最大值。对于每一个元素,其之前的子串的最大值和最小值要么是其自身,要么是之前的最大值、最小值乘以该元素。
public int maxProduct(int[] nums) {
int max = Integer.MIN_VALUE, imax = 1, imin = 1;
for(int i=0; i<nums.length; i++){
if(nums[i] < 0){ int tmp = imax; imax = imin; imin = tmp;}
//实际上imax从第一个正数开始累计,imin从第一个负数开始累计,遇到负数就交换
//如果有偶数个负数,会交换回从第一个开始计数的imax
//如果有奇数个负数,在最后一个奇数前是偶数个,imax还是从第一个开始计数的imax
//在最后一个奇数处再交换,imax变成从第一个负数后面的开始计数的imin,一直累计到最后
//因此两个imax和上面的方法的结果是一样的
imax = Math.max(imax*nums[i], nums[i]);
imin = Math.min(imin*nums[i], nums[i]);
max = Math.max(max, imax);
}
return max;
}