String · 15 of 43
Find next greater number with same set of digits
Shuffle the digits of a number to make the very next bigger number, by finding the rightmost dip, swapping in a slightly bigger digit, and flipping the tail.
- Simple way
- time O(n! * n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a number as a string of n digits. Rearrange its digits to make the smallest number that is still bigger than it. If no rearrangement is bigger, return -1 (or "not possible").
For 534976 the answer is 536479. For 4321 the digits already go down, which is the biggest order, so the answer is -1.
A story to remember it
Picture it
534976. From the right the digits go up 6, 7, 9 and then drop to 4. The 4 is the dip. The tail after it, 9 7 6, only goes down.
- looking now
- checking
Examples
A normal case
- In
n = '534976'- Out
536479
Dip at 4, swap with 6, flip 9 7 4 into 4 7 9.
The dip is near the front
- In
n = '218765'- Out
251678
Dip at 1, swap with 5, flip 8 7 6 1 into 1 6 7 8.
Last two digits
- In
n = '1234'- Out
1243
The dip is the 3. Swap with 4, and the tail is one digit, so nothing to flip.
Already the biggest
- In
n = '4321'- Out
-1
The digits only go down. No dip, so no bigger number exists.
Repeated digits
- In
n = '115'- Out
151
The dip is the second 1. Swap it with 5, then the tail is just 1.
One digit
- In
n = '9'- Out
-1
A single digit cannot be rearranged.
Way 1: the simple way
Make every arrangement of the digits and keep the smallest one that is bigger than n.
- Build all orders of the digits with backtracking.
- For each order that is bigger than
n, keep it if it is smaller than the best so far. - Return the best, or
-1.
class Solution {
private String best;
public String nextGreater(String n) {
best = null;
build(n.toCharArray(), new boolean[n.length()], new StringBuilder(), n);
return best == null ? "-1" : best;
}
private void build(char[] d, boolean[] used, StringBuilder cur, String n) {
if (cur.length() == d.length) {
String s = cur.toString();
// same length, so comparing as text is the same as comparing as numbers
if (s.compareTo(n) > 0 && (best == null || s.compareTo(best) < 0)) best = s;
return;
}
for (int k = 0; k < d.length; k++) {
if (used[k]) continue;
used[k] = true;
cur.append(d[k]);
build(d, used, cur, n); // try every digit in this place
cur.deleteCharAt(cur.length() - 1);
used[k] = false;
}
}
}How long it takes: n! orders, each built and compared in O(n), so O(n! * n). Ten digits already means over 36 million orders. Extra room: the order being built and the call stack, O(n).
Way 2: the clever way
Change as little as possible, as far to the right as possible.
- Walk from the right and find the first spot
iwhered[i] < d[i + 1]. That is the dip. Everything after it goes down. If there is no dip, return-1. - Walk from the right again and find the first digit
d[j]bigger thand[i]. Because the tail goes down, it is the smallest digit in the tail that is still bigger thand[i]. - Swap
d[i]andd[j]. The tail still goes down. - Flip the tail so it goes up. That makes it as small as possible.
Find the dip, swap, flip the tail
01 / 08
We want the next bigger number made of the same digits. Change as far to the right as possible, so start looking from the right end.
- looking now
- checking
- done
- answer
- just added
class Solution {
public String nextGreater(String n) {
char[] d = n.toCharArray();
int i = d.length - 2;
while (i >= 0 && d[i] >= d[i + 1]) i--; // find the dip from the right
if (i < 0) return "-1"; // digits only go down: no answer
int j = d.length - 1;
while (d[j] <= d[i]) j--; // smallest bigger digit in the tail
swap(d, i, j);
for (int a = i + 1, b = d.length - 1; a < b; a++, b--) {
swap(d, a, b); // flip the tail so it goes up
}
return new String(d);
}
private void swap(char[] d, int a, int b) {
char t = d[a]; d[a] = d[b]; d[b] = t;
}
}How long it takes: at most two walks and one flip over the digits, O(n). Extra room: the digit array, which is the answer itself, so O(1) extra.
Watch out for
- Use
>=when looking for the dip and<=when looking forj. Strict tests break on repeated digits: with>the search on1222stops at a fake dip and runs off the front, and with<the number151swaps a 1 with the other 1 and returns115. - When the input is an
int(LeetCode 556), the answer may not fit. The next arrangement of2147483486is2147483648, one more than the largestint, so the answer must be-1. Build it as alongand check. - No dip means the digits are in going down order, the biggest arrangement. Return
-1, do not wrap around.
What interviewers ask next
- Next permutation of an array in place (LeetCode 31)? The same steps. When there is no dip, flip the whole array to get the smallest arrangement.
- The previous smaller number? Mirror every step: find the first
d[i] > d[i + 1]from the right, swap with the biggest digit in the tail that is smaller thand[i], then flip the tail. - The k-th next arrangement, or the k-th arrangement of
1..n? Calling nextktimes is slow. Use counting: each first digit leads(n - 1)!arrangements, so pick digits with division and remainder. - Why is this the very next one? Everything left of the dip stays the same, the dip grows by the smallest possible step, and the tail becomes the smallest possible tail. Nothing can fit between.
Remember this
Grown-up words: an arrangement of the digits is a permutation, the very next arrangement is the next lexicographic permutation, the dip is the pivot, and the tail that only goes down is a non-increasing suffix.