Array Partition

Last Updated : 18 Aug, 2026

Given an array arr[] and two integers k and m, find whether it is possible to divide the array into one or more contiguous partitions (i.e., partitions must be made by splitting the array into consecutive, non-overlapping subarrays that together cover the whole array — no rearranging or skipping elements) such that:

  • Each element belongs to exactly one partition.
  • Each partition contains at least k elements.
  • For every partition, the difference between the maximum and minimum element in that partition does not exceed m.

Examples:

Input: arr[] = [1, 2, 5, 10], k = 2, m = 2
Output: false
Explanation: No valid partition can be formed such that:
Each partition contains at least 2 elements.
Difference between the maximum and minimum element in each partition does not exceed 2.

Input: arr[] = [14, 9, 10, 9, 4], k = 2, m = 9
Output: true
Explanation: We can partition the array into two partitions: [14, 9] and [10, 9, 4] such that all rules are satisfied.

Try It Yourself
redirect icon

[Naive Approach] Using Recursion - O(2 ^ n) Time and O(n) Space

The idea is to recursively try every possible partition. For each partition, maintain its minimum and maximum values and check whether the partition has at least k elements and max - min <= m.

Working of Approach:

  • Start from index 0 and try every possible ending index of the current partition.
  • Maintain the minimum and maximum element while extending the partition.
  • If the partition has at least k elements and max - min <= m, recursively process the remaining elements.
  • If any choice partitions the complete array, return true.
C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

bool canPartitionFrom(vector<int> &arr, int index, int k, int m)
{
    int n = arr.size();

    // If all elements are successfully partitioned,
    // return true.
    if (index == n)
        return true;

    int mn = INT_MAX;
    int mx = INT_MIN;

    // Try all possible ending positions
    // for the current partition.
    for (int i = index; i < n; i++)
    {
        mn = min(mn, arr[i]);
        mx = max(mx, arr[i]);

        // The current partition must contain
        // at least k elements.
        if (i - index + 1 < k)
            continue;

        // If the difference between maximum and minimum
        // exceeds m, extending the partition further
        // cannot make it valid.
        if (mx - mn > m)
            break;

        // Recursively check if the remaining elements
        // can also be partitioned successfully.
        if (canPartitionFrom(arr, i + 1, k, m))
            return true;
    }

    return false;
}

// Check whether the complete array can be partitioned
// into segments satisfying the given conditions.
bool canPartition(vector<int> &arr, int k, int m)
{
    return canPartitionFrom(arr, 0, k, m);
}

int main()
{
    vector<int> arr = {14, 9, 10, 9, 4};
    int k = 2;
    int m = 9;

    cout << (canPartition(arr, k, m) ? "true" : "false");

    return 0;
}
Java
import java.util.Arrays;

public class GFG {
    public static boolean
    canPartitionFrom(int[] arr, int index, int k, int m)
    {
        int n = arr.length;

        // If all elements are successfully partitioned,
        // return true.
        if (index == n)
            return true;

        int mn = Integer.MAX_VALUE;
        int mx = Integer.MIN_VALUE;

        // Try all possible ending positions
        // for the current partition.
        for (int i = index; i < n; i++) {
            mn = Math.min(mn, arr[i]);
            mx = Math.max(mx, arr[i]);

            // The current partition must contain
            // at least k elements.
            if (i - index + 1 < k)
                continue;

            // If the difference between maximum and minimum
            // exceeds m, extending the partition further
            // cannot make it valid.
            if (mx - mn > m)
                break;

            // Recursively check if the remaining elements
            // can also be partitioned successfully.
            if (canPartitionFrom(arr, i + 1, k, m))
                return true;
        }

        return false;
    }

    // Check whether the complete array can be partitioned
    // into segments satisfying the given conditions.
    public static boolean canPartition(int[] arr, int k,
                                       int m)
    {
        return canPartitionFrom(arr, 0, k, m);
    }

    public static void main(String[] args)
    {
        int[] arr = { 14, 9, 10, 9, 4 };
        int k = 2;
        int m = 9;

        System.out.println(
            canPartition(arr, k, m) ? "true" : "false");
    }
}
Python
def canPartitionFrom(arr, index, k, m):
    n = len(arr)

    # If all elements are successfully partitioned,
    # return true.
    if index == n:
        return True

    mn = float('inf')
    mx = float('-inf')

    # Try all possible ending positions
    # for the current partition.
    for i in range(index, n):
        mn = min(mn, arr[i])
        mx = max(mx, arr[i])

        # The current partition must contain
        # at least k elements.
        if i - index + 1 < k:
            continue

        # If the difference between maximum and minimum
        # exceeds m, extending the partition further
        # cannot make it valid.
        if mx - mn > m:
            break

        # Recursively check if the remaining elements
        # can also be partitioned successfully.
        if canPartitionFrom(arr, i + 1, k, m):
            return True

    return False

# Check whether the complete array can be partitioned
# into segments satisfying the given conditions.
def canPartition(arr, k, m):
    return canPartitionFrom(arr, 0, k, m)


if __name__ == '__main__':
    arr = [14, 9, 10, 9, 4]
    k = 2
    m = 9

    print('true' if canPartition(arr, k, m) else 'false')
C#
using System;

class GFG {
    static bool CanPartitionFrom(int[] arr, int index,
                                 int k, int m)
    {
        int n = arr.Length;

        // If all elements are successfully partitioned,
        // return true.
        if (index == n)
            return true;

        int mn = int.MaxValue;
        int mx = int.MinValue;

        // Try all possible ending positions
        // for the current partition.
        for (int i = index; i < n; i++) {
            mn = Math.Min(mn, arr[i]);
            mx = Math.Max(mx, arr[i]);

            // The current partition must contain
            // at least k elements.
            if (i - index + 1 < k)
                continue;

            // If the difference between maximum and minimum
            // exceeds m, extending the partition further
            // cannot make it valid.
            if (mx - mn > m)
                break;

            // Recursively check if the remaining elements
            // can also be partitioned successfully.
            if (CanPartitionFrom(arr, i + 1, k, m))
                return true;
        }

        return false;
    }

    // Check whether the complete array can be partitioned
    // into segments satisfying the given conditions.
    static bool canPartition(int[] arr, int k, int m)
    {
        return CanPartitionFrom(arr, 0, k, m);
    }

    static void Main()
    {
        int[] arr = { 14, 9, 10, 9, 4 };
        int k = 2;
        int m = 9;

        Console.WriteLine(
            canPartition(arr, k, m) ? "true" : "false");
    }
}
JavaScript
function canPartitionFrom(arr, index, k, m)
{
    let n = arr.length;

    // If all elements are successfully partitioned,
    // return true.
    if (index === n)
        return true;

    let mn = Number.MAX_SAFE_INTEGER;
    let mx = Number.MIN_SAFE_INTEGER;

    // Try all possible ending positions
    // for the current partition.
    for (let i = index; i < n; i++) {
        mn = Math.min(mn, arr[i]);
        mx = Math.max(mx, arr[i]);

        // The current partition must contain
        // at least k elements.
        if (i - index + 1 < k)
            continue;

        // If the difference between maximum and minimum
        // exceeds m, extending the partition further
        // cannot make it valid.
        if (mx - mn > m)
            break;

        // Recursively check if the remaining elements
        // can also be partitioned successfully.
        if (canPartitionFrom(arr, i + 1, k, m))
            return true;
    }

    return false;
}

// Check whether the complete array can be partitioned
// into segments satisfying the given conditions.
function canPartition(arr, k, m)
{
    return canPartitionFrom(arr, 0, k, m);
}

// Driver Code
let arr = [ 14, 9, 10, 9, 4 ];
let k = 2;
let m = 9;

console.log(canPartition(arr, k, m) ? "true" : "false");

Output
true

[Better Approach] Using Prefix Partition DP - O(n ^ 2) Time and O(n) Space

The idea is to use DP to determine whether the first i elements can be partitioned. For each ending position, try every possible starting position, maintain the minimum and maximum, and check whether the current partition is valid and the previous prefix is partitionable.

Working of Approach:

  • Let dp[i] indicate whether the first i elements can be partitioned validly.
  • For every ending position, try every possible starting position of the last partition.
  • Maintain the minimum and maximum values while extending the current partition.
  • If the partition contains at least k elements and max - min <= m, check whether dp[j - 1] is true.
  • If dp[j - 1] is true, mark dp[i] as true because the first j - 1 elements and the current partition together form a valid partition.
C++
#include <algorithm>
#include <climits>
#include <iostream>
#include <vector>
using namespace std;

bool canPartition(vector<int> &arr, int k, int m)
{
    int n = arr.size();

    if (n == 0)
        return true;

    if (k > n)
        return false;

    // dp[i] = true if the first i elements
    // can be partitioned validly.
    vector<bool> dp(n + 1, false);

    // Empty prefix is already valid.
    dp[0] = true;

    // Consider every ending position.
    for (int i = 1; i <= n; i++)
    {
        int mn = INT_MAX;
        int mx = INT_MIN;

        // Try every possible starting position
        // of the last partition.
        for (int j = i; j >= 1; j--)
        {
            mn = min(mn, arr[j - 1]);
            mx = max(mx, arr[j - 1]);

            // The current partition must contain
            // at least k elements.
            if (i - j + 1 < k)
                continue;

            // Difference between maximum and minimum
            // must not exceed m.
            if (mx - mn > m)
                break;

            // Check whether the previous prefix
            // can already be partitioned.
            if (dp[j - 1])
            {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

int main()
{
    vector<int> arr = {14, 9, 10, 9, 4};
    int k = 2;
    int m = 9;

    cout << (canPartition(arr, k, m) ? "true" : "false");

    return 0;
}
Java
import java.util.Arrays;

public class GFG {
    public static boolean canPartition(int[] arr, int k,
                                       int m)
    {
        int n = arr.length;

        if (n == 0)
            return true;

        if (k > n)
            return false;

        // dp[i] = true if the first i elements
        // can be partitioned validly.
        boolean[] dp = new boolean[n + 1];

        // Empty prefix is already valid.
        dp[0] = true;

        // Consider every ending position.
        for (int i = 1; i <= n; i++) {
            int mn = Integer.MAX_VALUE;
            int mx = Integer.MIN_VALUE;

            // Try every possible starting position
            // of the last partition.
            for (int j = i; j >= 1; j--) {
                mn = Math.min(mn, arr[j - 1]);
                mx = Math.max(mx, arr[j - 1]);

                // The current partition must contain
                // at least k elements.
                if (i - j + 1 < k)
                    continue;

                // Difference between maximum and minimum
                // must not exceed m.
                if (mx - mn > m)
                    break;

                // Check whether the previous prefix
                // can already be partitioned.
                if (dp[j - 1]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[n];
    }

    public static void main(String[] args)
    {
        int[] arr = { 14, 9, 10, 9, 4 };
        int k = 2;
        int m = 9;

        System.out.println(
            canPartition(arr, k, m) ? "true" : "false");
    }
}
Python
def canPartition(arr, k, m):
    n = len(arr)

    if n == 0:
        return True

    if k > n:
        return False

    # dp[i] = true if the first i elements
    # can be partitioned validly.
    dp = [False] * (n + 1)

    # Empty prefix is already valid.
    dp[0] = True

    # Consider every ending position.
    for i in range(1, n + 1):
        mn = float('inf')
        mx = float('-inf')

        # Try every possible starting position
        # of the last partition.
        for j in range(i, 0, -1):
            mn = min(mn, arr[j - 1])
            mx = max(mx, arr[j - 1])

            # The current partition must contain
            # at least k elements.
            if i - j + 1 < k:
                continue

            # Difference between maximum and minimum
            # must not exceed m.
            if mx - mn > m:
                break

            # Check whether the previous prefix
            # can already be partitioned.
            if dp[j - 1]:
                dp[i] = True
                break

    return dp[n]


if __name__ == '__main__':
    arr = [14, 9, 10, 9, 4]
    k = 2
    m = 9

    print('true' if canPartition(arr, k, m) else 'false')
C#
using System;

public class GFG {
    public static bool canPartition(int[] arr, int k, int m)
    {
        int n = arr.Length;

        if (n == 0)
            return true;

        if (k > n)
            return false;

        // dp[i] = true if the first i elements
        // can be partitioned validly.
        bool[] dp = new bool[n + 1];

        // Empty prefix is already valid.
        dp[0] = true;

        // Consider every ending position.
        for (int i = 1; i <= n; i++) {
            int mn = int.MaxValue;
            int mx = int.MinValue;

            // Try every possible starting position
            // of the last partition.
            for (int j = i; j >= 1; j--) {
                mn = Math.Min(mn, arr[j - 1]);
                mx = Math.Max(mx, arr[j - 1]);

                // The current partition must contain
                // at least k elements.
                if (i - j + 1 < k)
                    continue;

                // Difference between maximum and minimum
                // must not exceed m.
                if (mx - mn > m)
                    break;

                // Check whether the previous prefix
                // can already be partitioned.
                if (dp[j - 1]) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[n];
    }

    public static void Main()
    {
        int[] arr = { 14, 9, 10, 9, 4 };
        int k = 2;
        int m = 9;

        Console.WriteLine(
            canPartition(arr, k, m) ? "true" : "false");
    }
}
JavaScript
function canPartition(arr, k, m)
{
    let n = arr.length;

    if (n === 0)
        return true;

    if (k > n)
        return false;

    // dp[i] = true if the first i elements
    // can be partitioned validly.
    let dp = Array(n + 1).fill(false);

    // Empty prefix is already valid.
    dp[0] = true;

    // Consider every ending position.
    for (let i = 1; i <= n; i++) {
        let mn = Number.MAX_SAFE_INTEGER;
        let mx = Number.MIN_SAFE_INTEGER;

        // Try every possible starting position
        // of the last partition.
        for (let j = i; j >= 1; j--) {
            mn = Math.min(mn, arr[j - 1]);
            mx = Math.max(mx, arr[j - 1]);

            // The current partition must contain
            // at least k elements.
            if (i - j + 1 < k)
                continue;

            // Difference between maximum and minimum
            // must not exceed m.
            if (mx - mn > m)
                break;

            // Check whether the previous prefix
            // can already be partitioned.
            if (dp[j - 1]) {
                dp[i] = true;
                break;
            }
        }
    }

    return dp[n];
}

// Driver Code
let arr = [ 14, 9, 10, 9, 4 ];
let k = 2;
let m = 9;

console.log(canPartition(arr, k, m) ? "true" : "false");

Output
true

[Expected Approach] Using Optimized DP with Sliding Window and Segment Tree - O(n log n) Time and O(n) Space

The idea is to use a sliding window with monotonic deques to find the valid range of starting positions for each partition. Then, use DP with a Segment Tree to quickly check whether any previous partition point in this range is reachable.

Working of Approach:

  • Use two monotonic deques to maintain the minimum and maximum of the current window.
  • For every right index r, find the smallest valid starting index L[r] such that max - min <= m.
  • A partition ending at r must start between L[r] and r - k + 1.
  • Store valid DP states in the Segment Tree and query the required range in O(log n).
  • If a valid previous partition point exists, mark the current prefix as partitionable.

Let us understand with an example:
Input: arr[] = [14, 9, 10, 9, 4], k = 2, m = 9

  • Initially, the sliding window starts with left = 0.
  • For r = 0, the window [14] is valid, so L[0] = 0.
  • For r = 1, [14, 9] has max - min = 5, so L[1] = 0.
  • For r = 2, [14, 9, 10] has max - min = 5, so L[2] = 0.
  • For r = 3, [14, 9, 10, 9] has max - min = 5, so L[3] = 0.
  • For r = 4, max - min = 10 > 9, so left moves to 1; [9, 10, 9, 4] is valid, giving L[4] = 1.
  • Thus, L = [0, 0, 0, 0, 1].
  • Initially, dp[0] = true, so position 0 is updated in the Segment Tree.
  • For i = 2, the valid range is [0, 0], which contains dp[0], so dp[2] = true.
  • This forms the first partition [14, 9].
  • For i = 5, the valid range is [1, 3], which contains dp[2], so dp[5] = true.
  • This forms the second partition [10, 9, 4].
  • Since dp[5] = true, the complete array can be partitioned.

Output: true

C++
#include <algorithm>
#include <climits>
#include <deque>
#include <iostream>
#include <vector>
using namespace std;

class SegmentTree
{
    int n;
    vector<char> tree;

  public:
    SegmentTree(int size)
    {
        n = size;
        tree.assign(4 * max(n, 1), 0);
    }

    void update(int node, int start, int end, int pos, bool val)
    {
        if (start == end)
        {
            tree[node] = tree[node] | val;
            return;
        }
        int mid = (start + end) / 2;
        if (pos <= mid)
            update(2 * node, start, mid, pos, val);
        else
            update(2 * node + 1, mid + 1, end, pos, val);
        tree[node] = tree[2 * node] | tree[2 * node + 1];
    }

    void update(int pos, bool val)
    {
        if (n == 0)
            return;
        update(1, 0, n - 1, pos, val);
    }

    bool query(int node, int start, int end, int l, int r)
    {
        if (r < start || end < l || l > r)
            return false;
        if (l <= start && end <= r)
            return tree[node];
        int mid = (start + end) / 2;
        return query(2 * node, start, mid, l, r) || query(2 * node + 1, mid + 1, end, l, r);
    }

    bool query(int l, int r)
    {
        if (l > r || n == 0)
            return false;
        return query(1, 0, n - 1, l, r);
    }
};

bool canPartition(vector<int> &arr, int k, int m)
{
    int n = arr.size();
    if (n == 0)
        return true;
    if (k > n)
        return false;

    // Two-pointer + monotonic deques to find L(r) for every r
    // L[r] = smallest left index such that max(arr[L..r]) - min(arr[L..r]) <= m
    vector<int> L(n);
    deque<int> maxDq, minDq; // store indices
    int left = 0;

    for (int r = 0; r < n; r++)
    {
        while (!maxDq.empty() && arr[maxDq.back()] <= arr[r])
            maxDq.pop_back();
        maxDq.push_back(r);
        while (!minDq.empty() && arr[minDq.back()] >= arr[r])
            minDq.pop_back();
        minDq.push_back(r);

        while (arr[maxDq.front()] - arr[minDq.front()] > m)
        {
            left++;
            if (maxDq.front() < left)
                maxDq.pop_front();
            if (minDq.front() < left)
                minDq.pop_front();
        }
        L[r] = left;
    }

    // dp[i] : true if arr[0..i-1] can be validly partitioned
    SegmentTree segTree(n + 1);
    segTree.update(0, true); // dp[0] = true

    for (int i = 1; i <= n; i++)
    {
        int r = i - 1;
        int hi = i - k; // j must be <= i-k (size constraint)
        int lo = L[r];  // j must be >= L(r) (value constraint)
        if (hi >= lo && hi >= 0)
        {
            if (segTree.query(lo, hi))
            {
                segTree.update(i, true);
            }
        }
    }

    return segTree.query(n, n);
}

int main()
{

    vector<int> arr = {14, 9, 10, 9, 4};
    int k = 2;
    int m = 9;

    cout << (canPartition(arr, k, m) ? "true" : "false");

    return 0;
}
Java
import java.util.ArrayDeque;
import java.util.Deque;

class SegmentTree {
    int n;
    char[] tree;

    public SegmentTree(int size)
    {
        n = size;
        tree = new char[4 * Math.max(n, 1)];
    }

    void update(int node, int start, int end, int pos,
                boolean val)
    {
        if (start == end) {
            tree[node] = (char)(tree[node] | (val ? 1 : 0));
            return;
        }
        int mid = (start + end) / 2;
        if (pos <= mid)
            update(2 * node, start, mid, pos, val);
        else
            update(2 * node + 1, mid + 1, end, pos, val);
        tree[node]
            = (char)(tree[2 * node] | tree[2 * node + 1]);
    }

    void update(int pos, boolean val)
    {
        if (n == 0)
            return;
        update(1, 0, n - 1, pos, val);
    }

    boolean query(int node, int start, int end, int l,
                  int r)
    {
        if (r < start || end < l || l > r)
            return false;
        if (l <= start && end <= r)
            return tree[node] == 1;
        int mid = (start + end) / 2;
        return query(2 * node, start, mid, l, r)
            || query(2 * node + 1, mid + 1, end, l, r);
    }

    boolean query(int l, int r)
    {
        if (l > r || n == 0)
            return false;
        return query(1, 0, n - 1, l, r);
    }
}

public class GFG {
    public static boolean canPartition(int[] arr, int k,
                                       int m)
    {
        int n = arr.length;
        if (n == 0)
            return true;
        if (k > n)
            return false;

        // Two-pointer + monotonic deques to find L(r) for
        // every r L[r] = smallest left index such that
        // max(arr[L..r]) - min(arr[L..r]) <= m
        int[] L = new int[n];
        Deque<Integer> maxDq = new ArrayDeque<>(),
                       minDq = new ArrayDeque<>();
        int left = 0;

        for (int r = 0; r < n; r++) {
            while (!maxDq.isEmpty()
                   && arr[maxDq.peekLast()] <= arr[r])
                maxDq.pollLast();
            maxDq.addLast(r);
            while (!minDq.isEmpty()
                   && arr[minDq.peekLast()] >= arr[r])
                minDq.pollLast();
            minDq.addLast(r);

            while (!maxDq.isEmpty() && !minDq.isEmpty()
                   && arr[maxDq.peekFirst()]
                              - arr[minDq.peekFirst()]
                          > m) {
                left++;
                if (maxDq.peekFirst() < left)
                    maxDq.pollFirst();
                if (minDq.peekFirst() < left)
                    minDq.pollFirst();
            }
            L[r] = left;
        }

        // dp[i] : true if arr[0..i-1] can be validly
        // partitioned
        SegmentTree segTree = new SegmentTree(n + 1);
        segTree.update(0, true); // dp[0] = true

        for (int i = 1; i <= n; i++) {
            int r = i - 1;
            int hi
                = i
                  - k; // j must be <= i-k (size constraint)
            int lo = L[r]; // j must be >= L(r) (value
                           // constraint)
            if (hi >= lo && hi >= 0) {
                if (segTree.query(lo, hi))
                    segTree.update(i, true);
            }
        }

        return segTree.query(n, n);
    }

    public static void main(String[] args)
    {
        int[] arr = { 14, 9, 10, 9, 4 };
        int k = 2;
        int m = 9;

        System.out.println(
            canPartition(arr, k, m) ? "true" : "false");
    }
}
Python
from collections import deque


class SegmentTree:
    def __init__(self, size):
        self.n = size
        self.tree = [0] * (4 * max(self.n, 1))

    def update_recursive(self, node, start, end, pos, val):
        if start == end:
            self.tree[node] = self.tree[node] | val
            return

        mid = (start + end) // 2

        if pos <= mid:
            self.update_recursive(2 * node, start, mid, pos, val)
        else:
            self.update_recursive(2 * node + 1, mid + 1, end, pos, val)

        self.tree[node] = self.tree[2 * node] | self.tree[2 * node + 1]

    def update(self, pos, val):
        if self.n == 0:
            return

        self.update_recursive(1, 0, self.n - 1, pos, val)

    def query_recursive(self, node, start, end, l, r):
        if r < start or end < l or l > r:
            return False

        if l <= start and end <= r:
            return self.tree[node]

        mid = (start + end) // 2

        return (
            self.query_recursive(2 * node, start, mid, l, r)
            or
            self.query_recursive(2 * node + 1, mid + 1, end, l, r)
        )

    def query(self, l, r):
        if l > r or self.n == 0:
            return False

        return self.query_recursive(1, 0, self.n - 1, l, r)


def canPartition(arr, k, m):
    n = len(arr)

    if n == 0:
        return True

    if k > n:
        return False

    # Two-pointer + monotonic deques to find L[r] for every r.
    # L[r] = smallest left index such that
    # max(arr[L[r]..r]) - min(arr[L[r]..r]) <= m
    L = [0] * n

    maxDq = deque()
    minDq = deque()
    left = 0

    for r in range(n):

        while maxDq and arr[maxDq[-1]] <= arr[r]:
            maxDq.pop()

        maxDq.append(r)

        while minDq and arr[minDq[-1]] >= arr[r]:
            minDq.pop()

        minDq.append(r)

        while arr[maxDq[0]] - arr[minDq[0]] > m:
            left += 1

            if maxDq[0] < left:
                maxDq.popleft()

            if minDq[0] < left:
                minDq.popleft()

        L[r] = left

    # dp[i] : true if arr[0..i-1] can be validly partitioned
    segTree = SegmentTree(n + 1)

    # dp[0] = true
    segTree.update(0, True)

    for i in range(1, n + 1):
        r = i - 1

        # j must be <= i-k because partition size must be at least k.
        hi = i - k

        # j must be >= L[r] because of the value constraint.
        lo = L[r]

        if hi >= lo and hi >= 0:
            if segTree.query(lo, hi):
                segTree.update(i, True)

    return segTree.query(n, n)


if __name__ == "__main__":
    arr = [14, 9, 10, 9, 4]
    k = 2
    m = 9

    print("true" if canPartition(arr, k, m) else "false")
C#
using System;
using System.Collections.Generic;

public class SegmentTree
{
    private int n;
    private char[] tree;

    public SegmentTree(int size)
    {
        n = size;
        tree = new char[4 * Math.Max(n, 1)];
    }

    public void Update(int node, int start, int end, int pos, bool val)
    {
        if (start == end)
        {
            tree[node] = (char)(tree[node] | (val? '1' : '0'));
            return;
        }
        int mid = (start + end) / 2;
        if (pos <= mid)
            Update(2 * node, start, mid, pos, val);
        else
            Update(2 * node + 1, mid + 1, end, pos, val);
        tree[node] = (char)(tree[2 * node] | tree[2 * node + 1]);
    }

    public void Update(int pos, bool val)
    {
        if (n == 0)
            return;
        Update(1, 0, n - 1, pos, val);
    }

    public bool Query(int node, int start, int end, int l, int r)
    {
        if (r < start || end < l || l > r)
            return false;
        if (l <= start && end <= r)
            return tree[node] == '1';
        int mid = (start + end) / 2;
        return Query(2 * node, start, mid, l, r) || Query(2 * node + 1, mid + 1, end, l, r);
    }

    public bool Query(int l, int r)
    {
        if (l > r || n == 0)
            return false;
        return Query(1, 0, n - 1, l, r);
    }
}

public class GFG
{
    public static bool canPartition(int[] arr, int k, int m)
    {
        int n = arr.Length;
        if (n == 0)
            return true;
        if (k > n)
            return false;

        // Two-pointer + monotonic deques to find L(r) for every r
        // L[r] = smallest left index such that max(arr[L..r]) - min(arr[L..r]) <= m
        int[] L = new int[n];
        LinkedList<int> maxDq = new LinkedList<int>();
        LinkedList<int> minDq = new LinkedList<int>();
        int left = 0;

        for (int r = 0; r < n; r++)
        {
            while (maxDq.Count > 0 && arr[maxDq.Last.Value] <= arr[r])
                maxDq.RemoveLast();
            maxDq.AddLast(r);
            while (minDq.Count > 0 && arr[minDq.Last.Value] >= arr[r])
                minDq.RemoveLast();
            minDq.AddLast(r);

            while (arr[maxDq.First.Value] - arr[minDq.First.Value] > m)
            {
                left++;
                if (maxDq.First.Value < left)
                    maxDq.RemoveFirst();
                if (minDq.First.Value < left)
                    minDq.RemoveFirst();
            }
            L[r] = left;
        }

        // dp[i] : true if arr[0..i-1] can be validly partitioned
        SegmentTree segTree = new SegmentTree(n + 1);
        segTree.Update(0, true); // dp[0] = true

        for (int i = 1; i <= n; i++)
        {
            int r = i - 1;
            int hi = i - k; // j must be <= i-k (size constraint)
            int lo = L[r];  // j must be >= L(r) (value constraint)
            if (hi >= lo && hi >= 0)
            {
                if (segTree.Query(lo, hi))
                {
                    segTree.Update(i, true);
                }
            }
        }

        return segTree.Query(n, n);
    }

    public static void Main()
    {
        int[] arr = {14, 9, 10, 9, 4};
        int k = 2;
        int m = 9;

        Console.WriteLine(canPartition(arr, k, m)? "true" : "false");
    }
}
JavaScript
class SegmentTree {
    constructor(size)
    {
        this.n = size;
        this.tree
            = new Array(4 * Math.max(this.n, 1)).fill(0);
    }

    updateRecursive(node, start, end, pos, val)
    {
        if (start === end) {
            this.tree[node] = this.tree[node] | val;
            return;
        }

        let mid = Math.floor((start + end) / 2);

        if (pos <= mid) {
            this.updateRecursive(2 * node, start, mid, pos,
                                 val);
        }
        else {
            this.updateRecursive(2 * node + 1, mid + 1, end,
                                 pos, val);
        }

        this.tree[node]
            = this.tree[2 * node] | this.tree[2 * node + 1];
    }

    update(pos, val)
    {
        if (this.n === 0)
            return;

        this.updateRecursive(1, 0, this.n - 1, pos, val);
    }

    queryRecursive(node, start, end, l, r)
    {
        if (r < start || end < l || l > r)
            return false;

        if (l <= start && end <= r)
            return this.tree[node] !== 0;

        let mid = Math.floor((start + end) / 2);

        return (
            this.queryRecursive(2 * node, start, mid, l, r)
            || this.queryRecursive(2 * node + 1, mid + 1,
                                   end, l, r));
    }

    query(l, r)
    {
        if (l > r || this.n === 0)
            return false;

        return this.queryRecursive(1, 0, this.n - 1, l, r);
    }
}

function canPartition(arr, k, m)
{
    let n = arr.length;

    if (n === 0)
        return true;

    if (k > n)
        return false;

    // Two-pointer + monotonic deques to find L[r] for every
    // r. L[r] = smallest left index such that
    // max(arr[L[r]..r]) - min(arr[L[r]..r]) <= m
    let L = new Array(n).fill(0);

    let maxDq = [];
    let minDq = [];

    let maxFront = 0;
    let minFront = 0;

    let left = 0;

    for (let r = 0; r < n; r++) {

        while (maxDq.length > maxFront
               && arr[maxDq[maxDq.length - 1]] <= arr[r]) {
            maxDq.pop();
        }

        maxDq.push(r);

        while (minDq.length > minFront
               && arr[minDq[minDq.length - 1]] >= arr[r]) {
            minDq.pop();
        }

        minDq.push(r);

        while (arr[maxDq[maxFront]] - arr[minDq[minFront]]
               > m) {
            left++;

            if (maxDq[maxFront] < left)
                maxFront++;

            if (minDq[minFront] < left)
                minFront++;
        }

        L[r] = left;
    }

    // dp[i] : true if arr[0..i-1] can be validly
    // partitioned
    let segTree = new SegmentTree(n + 1);

    // dp[0] = true
    segTree.update(0, true);

    for (let i = 1; i <= n; i++) {
        let r = i - 1;

        // j must be <= i-k because partition size must be
        // at least k.
        let hi = i - k;

        // j must be >= L[r] because of the value
        // constraint.
        let lo = L[r];

        if (hi >= lo && hi >= 0) {
            if (segTree.query(lo, hi)) {
                segTree.update(i, true);
            }
        }
    }

    return segTree.query(n, n);
}

// Driver Code
let arr = [ 14, 9, 10, 9, 4 ];
let k = 2;
let m = 9;

console.log(canPartition(arr, k, m) ? "true" : "false");

Output
true
Comment