Longest Alternating Sequences — Problem Statement & Solution Guide

ArraysMediumMixed
TimeO(n)
|
SpaceO(1)

Quick Answer & Algorithm Key Takeaway

Master coding challenges related to Arrays and solve the Longest Alternating Sequences problem optimally.

TopicArrays
PatternMixed
TimeO(n)
SpaceO(1)

Problem Description

Given an integer array values, find two numbers. The first is the maximum length of a contiguous subsequence that first strictly increases and then strictly decreases (an “up‑down” segment). The second is the maximum length of a contiguous subsequence that first strictly decreases and then strictly increases (a “down‑up” segment). A segment may consist of only the increasing part or only the decreasing part, but the direction change, if present, must be strict. Return the two lengths as two space‑separated integers.

DSA Pattern Breakdown

DSA Pattern Breakdown

"Longest Alternating Sequences"

medium

WHY DOES IT MATTER?

Detecting up‑down or down‑up patterns is a core skill for recognizing piecewise monotonic behavior, which appears in signal processing, stock trend analysis, and performance profiling. Mastery of this pattern demonstrates the ability to convert a global optimization problem into local state tracking.

OPTIMIZATION CHALLENGE

The breakthrough is realizing that each element’s contribution to a candidate segment can be pre‑computed in two linear passes—forward for increasing lengths and backward for decreasing lengths—so the peak evaluation becomes O(1) per index instead of re‑scanning the whole subarray.

REAL-WORLD CONNECTION

Think of a server’s CPU load that ramps up during peak traffic and then cools down after the burst. Identifying the longest such ramp‑down cycle helps capacity planners allocate resources efficiently, analogous to finding the longest mountain in an array.

During an interview, compute the forward inc[] array on the fly while scanning, then immediately compute the backward dec[] array in a second pass; avoid storing both arrays if you can merge the second pass with the final max‑check to keep space O(1).

COMPLEXITY AT A GLANCE

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

Core Theory — Why This Approach?

The problem is a variant of the classic "longest mountain" challenge. A mountain is a contiguous subarray that strictly increases to a peak and then strictly decreases. To solve it efficiently we need to know, for each index, the length of the increasing run ending at that index and the length of the decreasing run starting at that index. Naïve enumeration of every possible subarray would be O(n²) and quickly exceeds time limits for large n because each candidate segment would be re‑scanned many times. The optimal paradigm leverages linear scans: one forward pass computes the length of the current increasing streak, and one backward pass computes the length of the current decreasing streak. By combining these two arrays we can evaluate every potential peak in O(1) time, yielding an overall O(n) solution. This approach also naturally yields the symmetric "down‑up" segment by swapping the direction of the monotonic checks, so both answers are obtained in a single pass pair.

Interview Questions on This Problem

Q1How would you modify the longest mountain algorithm to also return the start and end indices of the optimal up‑down segment?

Maintain the index where the current increasing streak began; when a decreasing streak ends, compute the total length and compare with the best. If it improves the best, store the start index (the beginning of the increasing streak) and the current index as the end.

Q2In a streaming context where the array is too large to fit in memory, can you compute the longest up‑down segment using O(1) extra space?

Yes. Use a two‑pointer sliding window that expands while the sequence is strictly increasing, then switches to decreasing. When monotonicity breaks, reset the window to start at the last peak. Track the maximum length seen. This works because only local monotonic information is needed.

Q3Why does the classic O(n) mountain solution fail if the array contains equal adjacent values, and how do you handle it?

The definition requires strict monotonicity; equal values break both increasing and decreasing runs, causing false peaks. The algorithm must reset the current streak counters whenever values are equal, treating them as a boundary between potential segments.

Examples

Example 1

Input

[1,3,5,4,2]

Output

5 2

Explanation: The subarray [1,3,5,4,2] strictly rises from 1 to 5 and then falls to 2, giving an up‑down length of 5. No longer down‑up pattern exists; the best we can do is any two adjacent elements such as [5,4], so the down‑up length is 2.

Example 2

Input

[9,7,5,6,8,10]

Output

2 6

Explanation: The longest up‑down segment is just the rise [5,6] or any two‑element rise, so its length is 2. The whole array [9,7,5,6,8,10] first falls from 9 to 5 and then rises to 10, giving a down‑up length of 6.

Example 3

Input

[4,4,4]

Output

1 1

Explanation: All elements are equal, so no strict increase or decrease can be formed. The only valid segments are single‑element subarrays, each of length 1, for both patterns.

Constraints

  • 1 <= values.length <= 100000
  • -1000000000 <= values[i] <= 1000000000
  • Solution must run in O(n) time and O(1) extra space

Optimal Approach & Strategy

Perform two linear passes to compute increasing and decreasing run lengths for each position, then combine them in a final linear scan to obtain the longest up‑down and down‑up segments—overall O(n) time.

Brute Force Approach

Check every possible subarray, verify if it first strictly increases then strictly decreases (or vice‑versa), and track the maximum length—this is O(n²).

Code Solutions

JavaScript Solution
Time: O(n)
function longestAlternatingSequences(values) {
    if (values.length === 0) return [0, 0];
    const n = values.length;
    const inc = new Array(n).fill(1);
    const dec = new Array(n).fill(1);
    for (let i = 1; i < n; ++i) {
        if (values[i] > values[i - 1]) inc[i] = inc[i - 1] + 1;
        if (values[i] < values[i - 1]) dec[i] = dec[i - 1] + 1;
    }
    let maxUpDown = 1, maxDownUp = 1;
    for (let i = 0; i < n; ++i) {
        const len = inc[i] + dec[i] - 1; // peak at i
        if (len > maxUpDown) maxUpDown = len;
    }
    // compute increasing run from right for down‑up
    const incR = new Array(n).fill(1);
    for (let i = n - 2; i >= 0; --i) {
        if (values[i] < values[i + 1]) incR[i] = incR[i + 1] + 1;
    }
    for (let i = 0; i < n; ++i) {
        const len = dec[i] + incR[i] - 1; // valley at i
        if (len > maxDownUp) maxDownUp = len;
    }
    return [maxUpDown, maxDownUp];
}

const readline = require('readline').createInterface({
    input: process.stdin,
    output: process.stdout
});
let lines = [];
readline.on('line', line => lines.push(line.trim()));
readline.on('close', () => {
    const n = parseInt(lines[0] || '0', 10);
    const values = (lines[1] || '').split(/\s+/).filter(s=>s.length).map(Number);
    const [upDown, downUp] = longestAlternatingSequences(values);
    console.log(upDown + ' ' + downUp);
});

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.