Given two integers n
and k
, return the k
th lexicographically smallest integer in the range [1, n]
. Lexicographical order implies sorting the numbers as strings.
For example, consider the range [1, 13]
. The lexicographical order would be [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9]
. If k = 2
, the second smallest number in this order is 10
.
Another example, given n = 1
and k = 1
, the output should be 1
.
Write a function that efficiently finds the k
th lexicographically smallest integer within the specified range. Consider that 1 <= k <= n <= 10^9
.
A naive approach would involve generating all integers from 1 to n
, sorting them lexicographically, and then returning the k
th element. However, this approach is inefficient, especially for large values of n
, as it requires storing all numbers in memory and sorting them.
def findKthLexicographical_naive(n, k):
nums = list(map(str, range(1, n + 1)))
nums.sort()
return int(nums[k - 1])
O(n log n), due to sorting n
elements.
O(n), to store the numbers in a list.
A more efficient approach is to use a depth-first search (DFS) strategy to traverse the lexicographical tree. We can determine how many numbers start with a given prefix. Based on this count, we can decide whether to move to the next prefix or to go deeper into the current prefix.
curr
to 1 (the starting number).k
times:
curr
and curr + 1
in the lexicographical order. We can name the number of steps as steps
.steps <= k
: It means the k
th number is not in the subtree of curr
, so we decrement k
by steps
and increment curr
by 1 (move to the next sibling).k
th number is in the subtree of curr
, so we decrement k
by 1 (move to the next child) and multiply curr
by 10.curr
.calSteps
This function calculates the number of integers between two numbers (inclusive) in lexicographical order. In other words, given n
and two numbers, n1
and n2
, how many numbers are between n1
and n2
which are less or equal to n
.
def findKthLexicographical(n, k):
curr = 1
k -= 1
while k > 0:
steps = calSteps(n, curr, curr + 1)
if steps <= k:
curr += 1
k -= steps
else:
curr *= 10
k -= 1
return curr
def calSteps(n, n1, n2):
steps = 0
while n1 <= n:
steps += min(n + 1, n2) - n1
n1 *= 10
n2 *= 10
return steps
Let's trace the optimal solution with n = 13
and k = 2
.
curr = 1
, k = 1
.steps = calSteps(13, 1, 2) = 11
(1, 10, 11, 12, 13).steps > k
, curr = 10
, k = 0
.curr = 10
.O(log n), where n
is the given integer. The calSteps
function takes O(1) since the number of steps is at most O(log n). The outer loop iterates until k = 0, and in the worst case, it would take O(log n) iterations to find the k-th lexicographical number.
O(1), as we use only a constant amount of extra space.
n = 1
, k = 1
: The function should return 1.n
is very large (e.g., 109): The algorithm should still work efficiently without causing overflow.k = 1
: The function should return 1.