简体   繁体   中英

FInd Next smallest number with same digits Python

I came across this challenge on codewars : https://www.codewars.com/kata/next-smaller-number-with-the-same-digits

It takes in an integer that can be very large. And return the next smallest integer with the same digits and returns -1 if it doesn't exist. I tried attempting this with brute-force, finding all permutations and finding the closest. This is obviously not efficient and results in a timeout.

What would be an efficient way of doing this?

  1. Starting from the right, find the first digit that has at least one smaller digit to its right. We'll call that digit X.
  2. Then find the largest digit that is to the right of X, and is smaller than X. We'll call that digit Y.
  3. Swap X and Y. This makes the number smaller.
  4. Then sort all of the digits to the right of Y in descending order. This makes the number as big as possible, without making it bigger than the original.

For example:

1262347  // original number
  ^  ^
  X  Y

1242367  // after the swap
  ^  ^
  Y  X

1247632  // after sorting digits to the right of Y
  ^
  Y

Here's a rookie attempt, and complexity is boundless, but still should be better than finding all possible combinations:

def swap(string, index1, index2):
    new_string = list(string)
    new_string[index1], new_string[index2] = string[index2], string[index1]
    new_string = sorted(new_string[:index1]) + new_string[index1:]
    return ''.join(new_string)

def smallest(number):
    if list(number) == sorted(number):
        return -1
    rev_num = number[::-1]
    for i, digit in enumerate(rev_num,0):
        if any(num for num in rev_num[:i] if num < digit):
            _, j = max(((num, j) for j, num in enumerate(rev_num[:i]) if int(num) < int(digit)),key = lambda x:(x[0], x[1]))
            swapped_num = swap(rev_num, i, j)
            if not swapped_num.endswith('0'):
                return int(swapped_num[::-1])
    return -1

x = ['29009','21','531','2071','9','111','135','1027','1231111111111111111111111111111111111111111111111111123456789']
for i in x:
    print(smallest(i))

Output:

20990
12
513
2017
-1
-1
-1
-1
1229876543311111111111111111111111111111111111111111111111111

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM