Make Array Strictly Increasing

Updated on 09 June, 2025
Make Array Strictly Increasing header image

Problem Statement

In this challenge, we are provided with two integer arrays, arr1 and arr2. The objective is to determine the minimum number of operations required to make the array arr1 strictly increasing. An array is strictly increasing if every element is greater than the one before it.

An operation is defined as selecting an element from arr1 and replacing it with any element from arr2. The indices used for this operation are denoted by i for arr1 and j for arr2, where 0 <= i < arr1.length and 0 <= j < arr2.length.

If it's impossible to reorder or replace the elements of arr1 to make it strictly increasing using the elements of arr2, the function should return -1.

Examples

Example 1

Input:

arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]

Output:

1

Explanation:

Replace `5` with `2`, then `arr1 = [1, 2, 3, 6, 7]`.

Example 2

Input:

arr1 = [1,5,3,6,7], arr2 = [4,3,1]

Output:

2

Explanation:

Replace `5` with `3` and then replace `3` with `4`. `arr1 = [1, 3, 4, 6, 7]`.

Example 3

Input:

arr1 = [1,5,3,6,7], arr2 = [1,6,3,3]

Output:

-1

Explanation:

You can't make `arr1` strictly increasing.

Constraints

  • 1 <= arr1.length, arr2.length <= 2000
  • 0 <= arr1[i], arr2[i] <= 10^9

Approach and Intuition

  1. The initial check would be to see if arr1 is already strictly increasing. If it is, return 0 since no operations are required.
  2. If not, we must consider how to make efficient replacements.
  3. One of the efficient approaches to solve this would involve dynamic programming or a greedy strategy:
    • A possible dynamic programming approach could be to maintain a table where dp[i] denotes the minimum number of changes required to make the subarray from arr1[0] to arr1[i] strictly increasing.
    • For each element in arr1, determine the possible replacements from arr2 that can make the sequence strictly increasing up to that point. This will typically require sorting arr2 and then binary searching for the smallest viable replacement for arr1[i].
    • Update the dp table accordingly by considering the previous optimal results (dp[i-1]).
  4. Throughout the updates, if at any point an element of arr1 cannot be made to comply with the strictly increasing condition despite all possible replacements from arr2, then return -1.
  5. If we successfully populate the dp table, the result for making the entire arr1 array strictly increasing will be found in the last entry (dp[arr1.length-1]).

Example walkthrough:

  • In Example 1 where arr1 = [1,5,3,6,7] and arr2 = [1,3,2,4], the replacement of 5 with the smaller 2 from arr2 allows us to tweak arr1 into a strictly increasing array with a single operation.
  • Example 2 and 3 illustrate situations with multiple required operations and an impossibility scenario, underscoring a need for a dynamic approach over merely a sequential check.

Solutions

  • Java
  • Python
java
class Solution {
    public int increaseArray(int[] arr1, int[] arr2) {
        Map<Integer, Integer> dpMap = new HashMap<>();
        Arrays.sort(arr2);
        int length = arr2.length;
        dpMap.put(-1, 0);

        for (int i = 0; i < arr1.length; i++) {
            Map<Integer, Integer> tempMap = new HashMap<>();
            for (int prevValue : dpMap.keySet()) {
                if (arr1[i] > prevValue) {
                    tempMap.put(arr1[i], Math.min(tempMap.getOrDefault(arr1[i], Integer.MAX_VALUE), dpMap.get(prevValue)));
                }
                int index = advancedBinarySearch(arr2, prevValue);
                if (index < length) {
                    tempMap.put(arr2[index], Math.min(tempMap.getOrDefault(arr2[index], Integer.MAX_VALUE), 1 + dpMap.get(prevValue)));
                }
            }
            dpMap = tempMap;
        }
        
        int minimalPath = Integer.MAX_VALUE;
        for (int value : dpMap.values()) {
            minimalPath = Math.min(minimalPath, value);
        }
        
        return minimalPath == Integer.MAX_VALUE ? -1 : minimalPath;
    }
    
    private static int advancedBinarySearch(int[] arr, int target) {
        int low = 0, high = arr.length;
        while (low < high) {
            int mid = (low + high) / 2;
            if (arr[mid] <= target) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return low;
    }
}

Implementing an algorithm to make an array strictly increasing involves designing a function that computes the minimal number of operations to reform an initial array arr1 into a strictly increasing sequence by substituting elements with those from a secondary sorted array arr2. The solution uses dynamic programming to store the minimum number of changes required using a hash map.

  • Define a method increaseArray to perform the operational logic on two given arrays arr1 and arr2.
  • Sort arr2 for efficient searching and comparison.
  • Utilize a hash map (dpMap) to store the required state of each element in the array, with keys as potential elements and values as the minimal operations to maintain strictly increasing order up to that element.
  • Use a nested loop structure to iterate over arr1:
    • In the outer loop, traverse each value of arr1.
    • Utilize an inner loop iterating over the keys of the dpMap to compare and update the map:
      • Compare current arr1 element with key values (representing previous elements). If greater, update tempMap for current arr1 element.
      • Perform a binary search (advancedBinarySearch) to find the smallest element in arr2 greater than the current key value and update the tempMap appropriately.
  • After constructing the map with potential successors, determine the minimal number of operations (minimalPath) by iterating over the values of dpMap.
  • The result is minimalPath if a solution is found; otherwise, return -1 if impossible to form a strictly increasing array.
  • Include a helper function advancedBinarySearch optimized for operations on sorted arrays to find the smallest number greater than a given target.

This approach efficiently maps the minimal operations needed to rearrange arr1 into a strictly increasing sequence using arr2 where necessary, leveraging sorting, hash maps, and binary search techiques. Ensure all edge cases, such as empty arrays and arrays where reordering is impossible, are handled gracefully by the function.

python
class Solution:
    def incrementArray(self, list1: List[int], list2: List[int]) -> int:
        state = {-1: 0}
        list2.sort()
        list2_size = len(list2)
        
        for index in range(len(list1)):
            new_state = collections.defaultdict(lambda: float('inf'))
            for prev_el in state:
                if list1[index] > prev_el:
                    new_state[list1[index]] = min(new_state[list1[index]], state[prev_el])
                next_index = bisect.bisect_right(list2, prev_el)
                if next_index < list2_size:
                    new_state[list2[next_index]] = min(new_state[list2[next_index]], 1 + state[prev_el])
            state = new_state

        return min(state.values()) if state else -1

Your task involves making an array strictly increasing by possibly replacing elements from the first array with elements from the second sorted array, keeping changes minimal. In Python, addressing this problem requires efficient manipulation of sequences and optimal state management throughout iteration.

The solution uses dynamic programming represented by a dictionary state that maps the last element of the current valid subsequence to the minimum number of changes needed to achieve that subsequence.

Here's how you tackle this problem:

  • Sort list2, since you need an ordered list to efficiently find the least element greater than a given value.
  • Initialize state with -1: 0, indicating that before starting, making 0 changes sets you before the start of list1.

For each element in list1:

  • Create a new dictionary new_state to store states for the current step.
  • Two main options to consider:
    • Continue the sequence without changes if the current list1 element is bigger than the previous ones in valid subsequence.
    • Use binary search (bisect.bisect_right) over list2 to find the smallest element that is larger than the last element in any valid subsequence you're considering.

Update state with new_state after considering both options for the current element.

The function returns the smallest value in state which gives the minimal changes needed. If no valid sequences are formed (state is empty), return -1.

Keep in mind:

  • The solution requires handling edge cases like both arrays being empty or list2 being unable to provide necessary elements to form a strictly increasing sequence.
  • Performance is optimized using binary search rather than a linear scan for updates, natively supported by Python’s bisect module.
  • The dynamic programming approach is memory efficient and only maintains relevant states necessary to decide the optimal number at each step.

Comments

No comments yet.