Nearest permutation to given array
First, build an ordered map of the counts of the distinct elements of A
.
Then, iterate forward through array indices (0 to n−1), "withdrawing" elements from this map. At each point, there are three possibilities:
- If
i < n-1
, and it's possible to chooseA[i] == B[i]
, do so and continue iterating forward. - Otherwise, if it's possible to choose
A[i] < B[i]
, choose the greatest possible value forA[i] < B[i]
. Then proceed by choosing the largest available values for all subsequent array indices. (At this point you no longer need to worry about maintainingA[i] <= B[i]
, because we're already after an index whereA[i] < B[i]
.) Return the result. - Otherwise, we need to backtrack to the last index where it was possible to choose
A[i] < B[i]
, then use the approach in the previous bullet-point.- Note that, despite the need for backtracking, the very worst case here is three passes: one forward pass using the logic in the first bullet-point, one backward pass in backtracking to find the last index where
A[i] < B[i]
was possible, and then a final forward pass using the logic in the second bullet-point.
- Note that, despite the need for backtracking, the very worst case here is three passes: one forward pass using the logic in the first bullet-point, one backward pass in backtracking to find the last index where
Because of the overhead of maintaining the ordered map, this requires O(n log m) time and O(m) extra space, where n is the total number of elements of A
and m is the number of distinct elements. (Since m ≤ n, we can also express this as O(n log n) time and O(n) extra space.)
Note that if there's no solution, then the backtracking step will reach all the way to i == -1
. You'll probably want to raise an exception if that happens.
Edited to add (2019-02-01):
In a now-deleted answer, גלעד ברקן summarizes the goal this way:
To be lexicographically smaller, the array must have an initial optional section from left to right where
A[i] = B[i]
that ends with an elementA[j] < B[j]
. To be closest toB
, we want to maximise the length of that section, and then maximise the remaining part of the array.
So, with that summary in mind, another approach is to do two separate loops, where the first loop determines the length of the initial section, and the second loop actually populates A
. This is equivalent to the above approach, but may make for cleaner code. So:
- Build an ordered map of the counts of the distinct elements of
A
. - Initialize
initial_section_length := -1
. - Iterate through the array indices 0 to n−1, "withdrawing" elements from this map. For each index:
- If it's possible to choose an as-yet-unused element of
A
that's less than the current element ofB
, setinitial_section_length
equal to the current array index. (Otherwise, don't.) - If it's not possible to choose an as-yet-unused element of
A
that's equal to the current element ofB
, break out of this loop. (Otherwise, continue looping.)
- If it's possible to choose an as-yet-unused element of
- If
initial_section_length == -1
, then there's no solution; raise an exception. - Repeat step #1: re-build the ordered map.
- Iterate through the array indices from 0 to
initial_section_length-1
, "withdrawing" elements from the map. For each index, choose an as-yet-unused element ofA
that's equal to the current element ofB
. (The existence of such an element is ensured by the first loop.) - For array index
initial_section_length
, choose the greatest as-yet-unused element ofA
that's less than the current element ofB
(and "withdraw" it from the map). (The existence of such an element is ensured by the first loop.) - Iterate through the array indices from
initial_section_length+1
to n−1, continuing to "withdraw" elements from the map. For each index, choose the greatest element ofA
that hasn't been used yet.
This approach has the same time and space complexities as the backtracking-based approach.
There are n!
permutations of A[n]
(less if there are repeating elements).
Use binary search over range 0..n!-1
to determine k-th lexicographic permutation of A[]
(arbitrary found example) which is closest lower one to B[]
.
Perhaps in C++ you can exploit std::lower_bound