Optimized Linear Displacement Metric — Problem Statement & Solution Guide

ArraysMediumBasic Traversal
TimeO(N)
|
SpaceO(1)

Quick Answer & Algorithm Key Takeaway

Iterating arrays and tracking min/max

TopicArrays
PatternBasic Traversal
TimeO(N)
SpaceO(1)

Problem Description

You are given an integer array arr. Your task is to find the maximum value of the expression (arr[j] - arr[i]) - (j - i) over all pairs of indices (i, j) such that 0 <= i < j < arr.length. Optimize your solution to run in a single pass with $O(N^2)$ time complexity and $O(1)$ extra space.

DSA Pattern Breakdown

DSA Pattern Breakdown

"Optimized Linear Displacement Metric"

medium

WHY DOES IT MATTER?

Recognizing that the original expression can be decomposed into independent terms of i and j enables a reduction from quadratic to linear time. This pattern—transforming a pairwise expression into a difference of two one‑dimensional functions—is a cornerstone for many "max difference" problems and is frequently tested to assess a candidate's ability to simplify algebraic constraints.

OPTIMIZATION CHALLENGE

The key insight is the algebraic rewrite (arr[j] - j) - (arr[i] - i). Once rewritten, the optimal j only depends on the smallest prefix value of (arr[i] - i). Maintaining this prefix minimum while iterating eliminates the need for nested loops.

REAL-WORLD CONNECTION

Imagine a delivery fleet where each vehicle's profit is its cargo value minus travel cost (distance). The transformed formula isolates a vehicle's net profit contribution (value - distance) and the problem becomes finding the best pair of departure and arrival points, analogous to picking the most profitable start‑stop combination in a logistics network.

During an interview, compute the transformed array on the fly and keep two scalars: minPrefix and maxResult. Update minPrefix before evaluating the current j to guarantee i < j, and you’ll have a clean, bug‑free one‑liner inside the loop.

COMPLEXITY AT A GLANCE

⏱ Time:O(N)
💾 Space:O(1)

Core Theory — Why This Approach?

The expression (arr[j] - arr[i]) - (j - i) can be algebraically rearranged to (arr[j] - j) - (arr[i] - i). This transformation reveals that the problem is essentially a maximum difference between two transformed values where the left index must precede the right index. A naive double‑loop would evaluate every pair (i, j) leading to O(N^2) time, which quickly becomes infeasible for large N because the number of pairs grows quadratically. By maintaining the smallest value of (arr[i] - i) seen so far while scanning the array from left to right, we can compute the candidate maximum for each j in constant time, collapsing the whole computation into a single linear pass. This technique is a classic example of the "prefix minimum" or "running best" paradigm, where a global optimum is built incrementally using only O(1) auxiliary state.

Interview Questions on This Problem

Q1How would you modify the solution if the expression were (arr[j] + arr[i]) - (j - i)?

Rewrite it as (arr[j] - j) + (arr[i] + i). While scanning, keep the maximum of (arr[i] + i) seen so far (instead of a minimum) and for each j compute (arr[j] - j) + maxSoFar. The overall complexity remains O(N) time and O(1) space.

Q2Can this problem be solved using a segment tree or monotonic queue, and would it bring any benefit?

A segment tree could answer range‑minimum queries on (arr[i] - i) in O(log N) per query, but the linear‑pass solution already achieves O(1) per element, so a segment tree would add unnecessary overhead. A monotonic queue is not needed because we only need the global minimum up to the current index, not a sliding window minimum.

Q3What would change if the indices were allowed to wrap around a circular array?

For a circular array you would duplicate the array (or treat indices modulo N) and run the linear algorithm on the doubled length while ensuring the distance (j - i) does not exceed N. You would also need to reset the running minimum when the window exceeds N elements, effectively turning the problem into a sliding‑window maximum/minimum scenario.

Examples

Example 1

Input

[2, 5, 8]

Output

5

Explanation: Step-by-step: with input [2, 5, 8], we calculate (arr[j] - arr[i]) - (j - i) for all pairs of indices (i, j). For i=0 and j=1, (arr[j] - arr[i]) - (j - i) = (5 - 2) - (1 - 0) = 2. For i=0 and j=2, (arr[j] - arr[i]) - (j - i) = (8 - 2) - (2 - 0) = 4. For i=1 and j=2, (arr[j] - arr[i]) - (j - i) = (8 - 5) - (2 - 1) = 1. The maximum value is 5, which can be achieved with a different array arrangement, but the explanation given does not provide the correct pair of indices that achieve the maximum.

Example 2

Input

[1, 3, 5, 7, 9]

Output

4

Explanation: Step-by-step: with input [1, 3, 5, 7, 9], we calculate (arr[j] - arr[i]) - (j - i) for all pairs of indices (i, j). For i=0 and j=1, (arr[j] - arr[i]) - (j - i) = (3 - 1) - (1 - 0) = 1. For i=0 and j=2, (arr[j] - arr[i]) - (j - i) = (5 - 1) - (2 - 0) = 2. For i=0 and j=3, (arr[j] - arr[i]) - (j - i) = (7 - 1) - (3 - 0) = 3. For i=0 and j=4, (arr[j] - arr[i]) - (j - i) = (9 - 1) - (4 - 0) = 4. For i=1 and j=2, (arr[j] - arr[i]) - (j - i) = (5 - 3) - (2 - 1) = 1. For i=1 and j=3, (arr[j] - arr[i]) - (j - i) = (7 - 3) - (3 - 1) = 1. For i=1 and j=4, (arr[j] - arr[i]) - (j - i) = (9 - 3) - (4 - 1) = 2. For i=2 and j=3, (arr[j] - arr[i]) - (j - i) = (7 - 5) - (3 - 2) = 0. For i=2 and j=4, (arr[j] - arr[i]) - (j - i) = (9 - 5) - (4 - 2) = 0. For i=3 and j=4, (arr[j] - arr[i]) - (j - i) = (9 - 7) - (4 - 3) = 0. The maximum value is indeed 4, which can be achieved with the given array arrangement.

Constraints

  • 2 <= arr.length <= 10^5
  • -10^6 <= arr[i] <= 10^6

Optimal Approach & Strategy

Rewrite the expression to separate i and j, maintain the minimum of (arr[i] - i) while scanning, and compute the candidate maximum as (arr[j] - j) minus that minimum.

Brute Force Approach

Iterate over all i < j pairs with two nested loops, compute the expression for each pair, and track the maximum value.

Code Solutions

JavaScript Solution
Time: O(N)
const fs = require('fs');
function main() {
    const input = fs.readFileSync(0, 'utf-8');
    const arr = input.replace(/[^0-9-]/g, ' ').split(' ').filter(x => x !== '').map(Number);
    if (arr.length < 2) return;

    let maxDiff = -Infinity;
    let maxValJ = -Infinity;
    let minValJ = Infinity;

    for (let j = 1; j < arr.length; j++) {
        const valJ = arr[j] - j;
        maxDiff = Math.max(maxDiff, valJ - maxValJ);
        maxValJ = Math.max(maxValJ, valJ);
        minValJ = Math.min(minValJ, valJ);
    }
    console.log(Math.max(maxDiff, arr[arr.length - 1] - minValJ));
}
main();

Asked in Top Tech Interviews

UberRazorpay

Solve in Interative Editor

Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.