Given an array of integers arr[]. Find two non-overlapping contiguous sub-arrays such that the absolute difference between the sum of two sub-arrays is maximum.
Examples:
Input: arr[] = [-2, -3, 4, -1, -2, 1, 5, -3]
Output: 12
Explanation: Two subarrays are [-2, -3] and [4, -1, -2, 1, 5].
Input: arr[] = [2, -1, -2, 1, -4, 2, 8]
Output: 16
Explanation: Two subarrays are [-1, -2, 1, -4] and [2, 8].
Table of Content
[Naive Approach] Check Every Pair of Non-Overlapping Subarrays - O(n ^ 4) Time and O(1) Space
The idea is to generate all possible pairs of non-overlapping subarrays. For every pair, compute the sum of both subarrays and calculate their absolute difference. The maximum such difference is the answer.
#include <iostream>
#include <vector>
using namespace std;
int maxDiffSubArrays(vector<int> &arr)
{
int n = arr.size();
int res = INT_MIN;
// Generate first subarray
for (int i = 0; i < n; i++)
{
int sum1 = 0;
for (int j = i; j < n; j++)
{
sum1 += arr[j];
// Generate second non-overlapping subarray
for (int k = j + 1; k < n; k++)
{
int sum2 = 0;
for (int l = k; l < n; l++)
{
sum2 += arr[l];
// Update maximum absolute difference
res = max(res, abs(sum1 - sum2));
}
}
}
}
// Generate subarrays in reverse order as well
for (int i = 0; i < n; i++)
{
int sum1 = 0;
for (int j = i; j < n; j++)
{
sum1 += arr[j];
// Generate second non-overlapping subarray
// on the left side
for (int k = 0; k < i; k++)
{
int sum2 = 0;
for (int l = k; l < i; l++)
{
sum2 += arr[l];
// Update maximum absolute difference
res = max(res, abs(sum1 - sum2));
}
}
}
}
return res;
}
int main()
{
vector<int> arr = {2, -1, -2, 1, -4, 2, 8};
cout << maxDiffSubArrays(arr);
return 0;
}
import java.util.Arrays;
public class GFG {
int maxDiffSubArrays(int[] arr) {
int n = arr.length;
int res = Integer.MIN_VALUE;
// Generate first subarray
for (int i = 0; i < n; i++) {
int sum1 = 0;
for (int j = i; j < n; j++) {
sum1 += arr[j];
// Generate second non-overlapping subarray
for (int k = j + 1; k < n; k++) {
int sum2 = 0;
for (int l = k; l < n; l++) {
sum2 += arr[l];
// Update maximum absolute difference
res = Math.max(res, Math.abs(sum1 - sum2));
}
}
}
}
// Generate subarrays in reverse order as well
for (int i = 0; i < n; i++) {
int sum1 = 0;
for (int j = i; j < n; j++) {
sum1 += arr[j];
// Generate second non-overlapping subarray
// on the left side
for (int k = 0; k < i; k++) {
int sum2 = 0;
for (int l = k; l < i; l++) {
sum2 += arr[l];
// Update maximum absolute difference
res = Math.max(res, Math.abs(sum1 - sum2));
}
}
}
}
return res;
}
public static void main(String[] args) {
GFG obj = new GFG();
int[] arr = {2, -1, -2, 1, -4, 2, 8};
System.out.println(obj.maxDiffSubArrays(arr));
}
}
def maxDiffSubArrays(arr):
n = len(arr)
res = float('-inf')
# Generate first subarray
for i in range(n):
sum1 = 0
for j in range(i, n):
sum1 += arr[j]
# Generate second non-overlapping subarray
for k in range(j + 1, n):
sum2 = 0
for l in range(k, n):
sum2 += arr[l]
# Update maximum absolute difference
res = max(res, abs(sum1 - sum2))
# Generate subarrays in reverse order as well
for i in range(n):
sum1 = 0
for j in range(i, n):
sum1 += arr[j]
# Generate second non-overlapping subarray
# on the left side
for k in range(i):
sum2 = 0
for l in range(k, i):
sum2 += arr[l]
# Update maximum absolute difference
res = max(res, abs(sum1 - sum2))
return res
if __name__ == '__main__':
arr = [2, -1, -2, 1, -4, 2, 8]
print(maxDiffSubArrays(arr))
using System;
public class GFG
{
public int maxDiffSubArrays(int[] arr)
{
int n = arr.Length;
int res = int.MinValue;
// Generate first subarray
for (int i = 0; i < n; i++)
{
int sum1 = 0;
for (int j = i; j < n; j++)
{
sum1 += arr[j];
// Generate second non-overlapping subarray
for (int k = j + 1; k < n; k++)
{
int sum2 = 0;
for (int l = k; l < n; l++)
{
sum2 += arr[l];
// Update maximum absolute difference
res = Math.Max(res, Math.Abs(sum1 - sum2));
}
}
}
}
// Generate subarrays in reverse order as well
for (int i = 0; i < n; i++)
{
int sum1 = 0;
for (int j = i; j < n; j++)
{
sum1 += arr[j];
// Generate second non-overlapping subarray
// on the left side
for (int k = 0; k < i; k++)
{
int sum2 = 0;
for (int l = k; l < i; l++)
{
sum2 += arr[l];
// Update maximum absolute difference
res = Math.Max(res, Math.Abs(sum1 - sum2));
}
}
}
}
return res;
}
public static void Main()
{
GFG obj = new GFG();
int[] arr = { 2, -1, -2, 1, -4, 2, 8 };
Console.WriteLine(obj.maxDiffSubArrays(arr));
}
}
function maxDiffSubArrays(arr) {
let n = arr.length;
let res = Number.MIN_SAFE_INTEGER;
// Generate first subarray
for (let i = 0; i < n; i++) {
let sum1 = 0;
for (let j = i; j < n; j++) {
sum1 += arr[j];
// Generate second non-overlapping subarray
for (let k = j + 1; k < n; k++) {
let sum2 = 0;
for (let l = k; l < n; l++) {
sum2 += arr[l];
// Update maximum absolute difference
res = Math.max(res, Math.abs(sum1 - sum2));
}
}
}
}
// Generate subarrays in reverse order as well
for (let i = 0; i < n; i++) {
let sum1 = 0;
for (let j = i; j < n; j++) {
sum1 += arr[j];
// Generate second non-overlapping subarray
// on the left side
for (let k = 0; k < i; k++) {
let sum2 = 0;
for (let l = k; l < i; l++) {
sum2 += arr[l];
// Update maximum absolute difference
res = Math.max(res, Math.abs(sum1 - sum2));
}
}
}
}
return res;
}
// Driver code
let arr = [2, -1, -2, 1, -4, 2, 8];
console.log(maxDiffSubArrays(arr));
Output
16
[Expected Approach] Kadane's Algorithm with Array Inversion - O(n) Time and O(n) Space
The idea is to partition the array at every position and maximize the difference between two non-overlapping subarrays. We use Kadane's algorithm to precompute maximum and minimum subarray sums on both sides of every partition, then evaluate both possible differences and take the maximum.
Let us understand with example:
- For arr = [2, -1, -2, 1, -4, 2, 8], Kadane's algorithm builds leftMax = [2, 2, 2, 2, 2, 2, 10] and rightMax = [10, 10, 10, 10, 10, 10, 8].
- After inverting the array and applying the same logic, we obtain leftMin = [2, -1, -3, -3, -6, -6, -6] and rightMin = [-6, -6, -6, -4, -4, 2, 8].
- Consider the partition after index 4. The left part is [2, -1, -2, 1, -4] and the right part is [2, 8].
- Here, leftMin[4] = -6 and rightMax[5] = 10, so the absolute difference becomes |-6 - 10| = 16.
- No other partition gives a larger value, so the maximum absolute difference is 16.
#include <iostream>
#include <vector>
using namespace std;
// Function to build maximum subarray sums from left to right
// leftMax[i] stores the maximum subarray sum in arr[0...i]
vector<int> buildLeftMax(vector<int> &arr)
{
int n = arr.size();
vector<int> leftMax(n);
int curr = arr[0];
int best = arr[0];
leftMax[0] = best;
for (int i = 1; i < n; i++)
{
// Apply Kadane's algorithm
curr = max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = max(best, curr);
leftMax[i] = best;
}
return leftMax;
}
// Function to build maximum subarray sums from right to left
// rightMax[i] stores the maximum subarray sum in arr[i...n-1]
vector<int> buildRightMax(vector<int> &arr)
{
int n = arr.size();
vector<int> rightMax(n);
int curr = arr[n - 1];
int best = arr[n - 1];
rightMax[n - 1] = best;
for (int i = n - 2; i >= 0; i--)
{
// Apply Kadane's algorithm in reverse direction
curr = max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = max(best, curr);
rightMax[i] = best;
}
return rightMax;
}
int maxDiffSubArrays(vector<int> &arr)
{
int n = arr.size();
// Maximum subarray sums on left and right side
vector<int> leftMax = buildLeftMax(arr);
vector<int> rightMax = buildRightMax(arr);
// Create inverted array to obtain minimum subarray sums
vector<int> inverted(arr);
for (int &x : inverted)
x = -x;
// Maximum subarray on inverted array corresponds
// to minimum subarray on original array
vector<int> leftMin = buildLeftMax(inverted);
vector<int> rightMin = buildRightMax(inverted);
// Convert values back to minimum subarray sums
for (int &x : leftMin)
x = -x;
for (int &x : rightMin)
x = -x;
int res = INT_MIN;
// Try every possible partition point
for (int i = 0; i < n - 1; i++)
{
// Maximum subarray on left and minimum subarray on right
int option1 = abs(leftMax[i] - rightMin[i + 1]);
// Minimum subarray on left and maximum subarray on right
int option2 = abs(leftMin[i] - rightMax[i + 1]);
// Update answer
res = max(res, max(option1, option2));
}
return res;
}
int main()
{
vector<int> arr = {2, -1, -2, 1, -4, 2, 8};
cout << maxDiffSubArrays(arr);
return 0;
}
class GFG {
// Function to build maximum subarray sums from left to
// right. leftMax[i] stores the maximum subarray sum in
// arr[0...i]
static int[] buildLeftMax(int[] arr)
{
int n = arr.length;
int[] leftMax = new int[n];
int curr = arr[0];
int best = arr[0];
leftMax[0] = best;
for (int i = 1; i < n; i++) {
// Apply Kadane's algorithm
curr = Math.max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.max(best, curr);
leftMax[i] = best;
}
return leftMax;
}
// Function to build maximum subarray sums from right to
// left. rightMax[i] stores the maximum subarray sum in
// arr[i...n-1]
static int[] buildRightMax(int[] arr)
{
int n = arr.length;
int[] rightMax = new int[n];
int curr = arr[n - 1];
int best = arr[n - 1];
rightMax[n - 1] = best;
for (int i = n - 2; i >= 0; i--) {
// Apply Kadane's algorithm in reverse
// direction
curr = Math.max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.max(best, curr);
rightMax[i] = best;
}
return rightMax;
}
static int maxDiffSubArrays(int[] arr)
{
int n = arr.length;
// Maximum subarray sums on left and right side
int[] leftMax = buildLeftMax(arr);
int[] rightMax = buildRightMax(arr);
// Create inverted array to obtain minimum subarray
// sums
int[] inverted = arr.clone();
for (int i = 0; i < n; i++)
inverted[i] = -inverted[i];
// Maximum subarray on inverted array corresponds
// to minimum subarray on original array
int[] leftMin = buildLeftMax(inverted);
int[] rightMin = buildRightMax(inverted);
// Convert values back to minimum subarray sums
for (int i = 0; i < n; i++)
leftMin[i] = -leftMin[i];
for (int i = 0; i < n; i++)
rightMin[i] = -rightMin[i];
int res = Integer.MIN_VALUE;
// Try every possible partition point
for (int i = 0; i < n - 1; i++) {
// Maximum subarray on left and minimum subarray
// on right
int option1
= Math.abs(leftMax[i] - rightMin[i + 1]);
// Minimum subarray on left and maximum subarray
// on right
int option2
= Math.abs(leftMin[i] - rightMax[i + 1]);
// Update answer
res = Math.max(res, Math.max(option1, option2));
}
return res;
}
public static void main(String[] args)
{
int[] arr = { 2, -1, -2, 1, -4, 2, 8 };
System.out.println(maxDiffSubArrays(arr));
}
}
import sys
# Function to build maximum subarray sums from left to right
# leftMax[i] stores the maximum subarray sum in arr[0...i]
def buildLeftMax(arr):
n = len(arr)
leftMax = [0] * n
curr = arr[0]
best = arr[0]
leftMax[0] = best
for i in range(1, n):
# Apply Kadane's algorithm
curr = max(arr[i], curr + arr[i])
# Update best maximum subarray sum seen so far
best = max(best, curr)
leftMax[i] = best
return leftMax
# Function to build maximum subarray sums from right to left
# rightMax[i] stores the maximum subarray sum in arr[i...n-1]
def buildRightMax(arr):
n = len(arr)
rightMax = [0] * n
curr = arr[n - 1]
best = arr[n - 1]
rightMax[n - 1] = best
for i in range(n - 2, -1, -1):
# Apply Kadane's algorithm in reverse direction
curr = max(arr[i], curr + arr[i])
# Update best maximum subarray sum seen so far
best = max(best, curr)
rightMax[i] = best
return rightMax
def maxDiffSubArrays(arr):
n = len(arr)
# Maximum subarray sums on left and right side
leftMax = buildLeftMax(arr)
rightMax = buildRightMax(arr)
# Create inverted array to obtain minimum subarray sums
inverted = [-x for x in arr]
# Maximum subarray on inverted array corresponds
# to minimum subarray on original array
leftMin = buildLeftMax(inverted)
rightMin = buildRightMax(inverted)
# Convert values back to minimum subarray sums
leftMin = [-x for x in leftMin]
rightMin = [-x for x in rightMin]
res = -sys.maxsize
# Try every possible partition point
for i in range(n - 1):
# Maximum subarray on left and minimum subarray on right
option1 = abs(leftMax[i] - rightMin[i + 1])
# Minimum subarray on left and maximum subarray on right
option2 = abs(leftMin[i] - rightMax[i + 1])
# Update answer
res = max(res, max(option1, option2))
return res
if __name__ == '__main__':
arr = [2, -1, -2, 1, -4, 2, 8]
print(maxDiffSubArrays(arr))
using System;
class GFG {
// Function to build maximum subarray sums from left to
// right. leftMax[i] stores the maximum subarray sum in
// arr[0...i]
static int[] buildLeftMax(int[] arr)
{
int n = arr.Length;
int[] leftMax = new int[n];
int curr = arr[0];
int best = arr[0];
leftMax[0] = best;
for (int i = 1; i < n; i++) {
// Apply Kadane's algorithm
curr = Math.Max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.Max(best, curr);
leftMax[i] = best;
}
return leftMax;
}
// Function to build maximum subarray sums from right to
// left. rightMax[i] stores the maximum subarray sum in
// arr[i...n-1]
static int[] buildRightMax(int[] arr)
{
int n = arr.Length;
int[] rightMax = new int[n];
int curr = arr[n - 1];
int best = arr[n - 1];
rightMax[n - 1] = best;
for (int i = n - 2; i >= 0; i--) {
// Apply Kadane's algorithm in reverse
// direction
curr = Math.Max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.Max(best, curr);
rightMax[i] = best;
}
return rightMax;
}
static int maxDiffSubArrays(int[] arr)
{
int n = arr.Length;
// Maximum subarray sums on left and right side
int[] leftMax = buildLeftMax(arr);
int[] rightMax = buildRightMax(arr);
// Create inverted array to obtain minimum subarray
// sums
int[] inverted = (int[])arr.Clone();
for (int i = 0; i < n; i++)
inverted[i] = -inverted[i];
// Maximum subarray on inverted array corresponds
// to minimum subarray on original array
int[] leftMin = buildLeftMax(inverted);
int[] rightMin = buildRightMax(inverted);
// Convert values back to minimum subarray sums
for (int i = 0; i < n; i++)
leftMin[i] = -leftMin[i];
for (int i = 0; i < n; i++)
rightMin[i] = -rightMin[i];
int res = int.MinValue;
// Try every possible partition point
for (int i = 0; i < n - 1; i++) {
// Maximum subarray on left and minimum subarray
// on right
int option1
= Math.Abs(leftMax[i] - rightMin[i + 1]);
// Minimum subarray on left and maximum subarray
// on right
int option2
= Math.Abs(leftMin[i] - rightMax[i + 1]);
// Update answer
res = Math.Max(res, Math.Max(option1, option2));
}
return res;
}
static void Main()
{
int[] arr = { 2, -1, -2, 1, -4, 2, 8 };
Console.WriteLine(maxDiffSubArrays(arr));
}
}
// Function to build maximum subarray sums from left to
// right. leftMax[i] stores the maximum subarray sum in
// arr[0...i]
function buildLeftMax(arr)
{
const n = arr.length;
const leftMax = new Array(n);
let curr = arr[0];
let best = arr[0];
leftMax[0] = best;
for (let i = 1; i < n; i++) {
// Apply Kadane's algorithm
curr = Math.max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.max(best, curr);
leftMax[i] = best;
}
return leftMax;
}
// Function to build maximum subarray sums from right to
// left. rightMax[i] stores the maximum subarray sum in
// arr[i...n-1]
function buildRightMax(arr)
{
const n = arr.length;
const rightMax = new Array(n);
let curr = arr[n - 1];
let best = arr[n - 1];
rightMax[n - 1] = best;
for (let i = n - 2; i >= 0; i--) {
// Apply Kadane's algorithm in reverse direction
curr = Math.max(arr[i], curr + arr[i]);
// Update best maximum subarray sum seen so far
best = Math.max(best, curr);
rightMax[i] = best;
}
return rightMax;
}
function maxDiffSubArrays(arr)
{
const n = arr.length;
// Maximum subarray sums on left and right side
const leftMax = buildLeftMax(arr);
const rightMax = buildRightMax(arr);
// Create inverted array to obtain minimum subarray
// sums.
const inverted = arr.map(x => -x);
// Maximum subarray on inverted array corresponds
// to minimum subarray on original array
const leftMin = buildLeftMax(inverted);
const rightMin = buildRightMax(inverted);
// Convert values back to minimum subarray sums
const leftMinConverted = leftMin.map(x => -x);
const rightMinConverted = rightMin.map(x => -x);
let res = Number.MIN_SAFE_INTEGER;
// Try every possible partition point
for (let i = 0; i < n - 1; i++) {
// Maximum subarray on left and minimum subarray on
// right
const option1 = Math.abs(
leftMax[i] - rightMinConverted[i + 1]);
// Minimum subarray on left and maximum subarray on
// right
const option2 = Math.abs(leftMinConverted[i]
- rightMax[i + 1]);
// Update answer
res = Math.max(res, Math.max(option1, option2));
}
return res;
}
// Driver Code
const arr = [ 2, -1, -2, 1, -4, 2, 8 ];
console.log(maxDiffSubArrays(arr));
Output
16