I have a porblem that I almost solved it but I'm facing problem with one test case, giving an array of numbers, it is allowed to choose any number and swap it digits so the array become strictly increasing, for example: [2, 4, 800, 12], we can choose 800 and convert it to 008 then array become [2,4,008,12], so it should return true, and it is allowed to it once, so the task is to check if it possible to convert an array to strictly increasing with at most one swap, here is my code
def solution(numbers):
swapped = 0
for i in range(len(numbers)-1):
if numbers[i] > numbers[i 1]:
if swapped >= 1:
return False
s1= int(''.join(sorted(str(numbers[i]))))
if s1 < numbers[i 1]:
if i > 0 and s1 >= numbers[i-1]:
numbers[i] = s1
swapped = 1
continue
s2 = int(''.join(sorted(str(numbers[i 1]), reverse=True)))
if s2 >= numbers[i]:
numbers[i 1] = s2
swapped = 1
continue
return True
the only test case the code doesn't pass is [13,31,30] (returning True
instead of False
) and I don't know how to solve this problem.
(apologies for any an un-elegant parts in my question, I'm new to this world and I'm trying my best)
CodePudding user response:
Your logic is largely right, except that I would put the check for the swap at the end of the loop, and I would check all the permutations instead of just the smallest and largest integer created from the digits.
from itertools import permutations, pairwise
def soln(numbers):
swapped = 0
for i in range(len(numbers)):
perms = list(int(''.join(shuffled_digits)) for shuffled_digits in permutations(str(numbers[i])))
if i == 0:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number < numbers[i 1]]
elif i < len(numbers) - 1:
candidate_nums = [shuffled_number for shuffled_number in perms if numbers[i-1] < shuffled_number < numbers[i 1]]
else:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number > numbers[i-1]]
if candidate_nums:
swapped_num = candidate_nums.pop()
numbers[i] = swapped_num
swapped =1
if all(x < y for x, y in pairwise(numbers)):
return True
return False
Output
In [80]: inlist
Out[80]: [2, 4, 8, 12]
In [81]: soln(inlist)
Out[81]: True
In [82]: inlist = [2, 4, 800, 12]
In [83]: soln(inlist)
Out[83]: True
In [84]: inlist = [21, 21]
In [85]: soln(inlist)
Out[85]: True
In [86]: inlist = [2, 1, 9, 7]
In [87]: soln(inlist)
Out[87]: False
In [88]: inlist = [2, 1, 1]
In [89]: soln(inlist)
Out[89]: False
In [90]: inlist = [100, 2, 3]
In [91]: soln(inlist)
Out[91]: True
In [92]: inlist = [52, 53, 36]
In [93]: soln(inlist)
Out[93]: True
In [94]: inlist = [44,37,74]
In [95]: soln(inlist)
Out[95]: True
CodePudding user response:
When you have a candidate number that needs to change, I would suggest making a directed search in the (virtual) list of permutations to find the permutation whose value is as close as possible to the value you compared with in order to restore the strictly ascending order.
It is not necessary to generate all permutations for this, which would consume too much time and space when the number of digits in the input numbers exceeds 10.
The idea is to count the number of digits you have of each digit, in the number that needs a permutation, and then take the next digit for building a permutation in such a way that you stay as close as possible to the target value. Some backtracking is needed, but for each digit position at most 2 attempts are needed: one for a digit that exactly matches the digit in the target (the value to compare with), and potentially a second attempt with another digit.
Here is an implementation of that idea.
First a helper function to find a permutation of a given value that is as near as possible to a target value (but not equal to it), and must be either less or greater (last argument controls this):
def permute_near(value, target, side=1):
# If side is 1, we will look for the minimum permutation of value that is
# greater than the target.
# If side is -1, we will look for the maximum permutation of value that is
# less than the target.
freedigit = 0 if side == 1 else 9
end = freedigit 10*side
target = side
# Get a count for each digit of value (could also use Counter module for this)
s = str(value)
n = len(s)
digits = [0]*10
for digit in s:
digits[int(digit)] = 1
# Pad the target value to have the same number of digits
limit = [int(digit) for digit in str(target).rjust(n, "0")]
# Use recursion to find the permutation of value that is nearest to the target
def recur(k, isfree, accumulator):
if k >= n: # All digits were processed, we have the result
return accumulator
limitdigit = limit[k]
for i in range(freedigit if isfree else limitdigit, end, side):
if digits[i]:
digits[i] -= 1 # Select this digit for the permutation
permutation = recur(k 1, isfree or i != limitdigit, accumulator * 10 i)
digits[i] = 1 # Backtrack
if permutation > -1 or i != limitdigit:
return permutation
# If it failed for the case where the chosen digit was the same
# as in the target, try with one alternative digit in a
# second and also last iteration of this loop.
return -1
return recur(0, False, 0)
Here is the main function:
def solution(numbers):
try:
# Find the first index where the next value is not greater
i = next(i for i, val in enumerate(numbers) if val >= numbers[i 1])
except:
return numbers # The input is already completely ascending
# Permute numbers[i], so it becomes less than its successor
result = permute_near(numbers[i], numbers[i 1], -1)
# Check that such permutation exists, and is still greater than its predecessor
if result >= 0 and (i == 0 or numbers[i - 1] < result):
numbers[i] = result # OK, we have success
else:
# ... or permute numbers[i 1], so it becomes greater than its predecessor
result = permute_near(numbers[i 1], numbers[i], 1)
# Check that such permutation exists, and is still less than its successor
if result >= 0 and (i 2 >= len(numbers) or result < numbers[i 2]):
numbers[i 1] = result # OK, we have success
if all(a < b for a, b in zip(numbers, numbers[1:])):
return numbers
Some runs:
# A few test cases
print(solution([2,4,800,12])) # [2, 4, 8, 12]
print(solution([44,37,74])) # [44, 73, 74]
print(solution([52,53,36])) # [52, 53, 63]
print(solution([64,52,53])) # [46, 52, 53]
print(solution([89221173000,89221173456,13437127829,89221173477]))
# [89221173000, 89221173456, 89221173473, 89221173477]
The function returns the adjusted list if there is a solution, None otherwise.
CodePudding user response:
The code mentioned in the question also fails for this testcase: [52,31,30]
Here's the solution that returns True if there's atleast one increasing array / list by swapping only one number.
def solution(numbers):
# Converts to str
numbers = [str(i) for i in numbers]
n = len(numbers)
for j in range(n 1):
if j != n:
# Reverse number
numbers[j] = str(numbers[j])[::-1]
# Checks if the array is in increasing order
if all([int(numbers[i 1]) > int(numbers[i]) for i in range(n - 1)]):
return True
if j != n:
numbers[j] = str(numbers[j])[::-1]
return False