Leetcode Daily Question 22/09/2024 - 440. K-th Smallest in Lexicographical Order
Problem Description:
Given integers n, k. Find the kth smallest lexicographic number between [1,n]
Solution:
class Solution:
def countNums(self, current, n):
count = 0
first = current
last = current + 1
while first <= n:
count += min(n+1, last) - first
first *= 10
last *= 10
return count
def findKthNumber(self, n: int, k: int) -> int:
current = 1
k -= 1
while k > 0:
count = self.countNums(current, n)
if count <= k:
k -= count
current += 1
else:
k -= 1
current *= 10
return current
Explanation:
We solve this by counting how many lexicographic numbers (l-nums) are between two consecutive numbers between 1 and possibly 9. This is because l-nums only concern the least significant digit in the number.
For example if we have n = 13. Then the lexico order would be [1,10,11,12,13,2,3,4,5,6,7,8,9].
Here is how the counting works. We start with 1. Between 1 and 2, we need to consider all possible l-nums.
We start with a trivial count at 1. Then we append a digit by multiplying 10 to 1 and 2. So now between 10 and 20, we see that 20 is larger than n, so truncate our count to end at n. This means we need to add n - lower + 1 to our count = 13 + 1 - 10 + (1 from before) = 5.
def countNums(current, n):
count = 0
first = current
lsat = current + 1
while first <= n:
count += min(n + 1, last) - first
first *= 10
last *= 10
return count
Now, suppose k = 2, n = 13 as before. Since our counting in 0-indexed, we need to start with k - 1 = 1. We also initiate current = 1
Then while k is not 0, we count how many l-nums are between current and current + 1.
We have 5 l-nums between [1,2), this is larger than k = 1, so we move on to start from 10 and decrement k. However k is now 0, so loop breaks and our final answer is obtained.
Consider if k = 3 instead. Then we would have k = 1 current = 10 at the end of step 1 as before. Step 2, we see that there are 1 l-nums between [10,11) this is equal to k so current = 11 and we subtract count from k to break the loop.
def findKthNumber(n, k):
current = 1
k -= 1
while k > 0:
count = countNums(current, n)
if count <= k:
current += 1
k -= count
else:
current *= 10
k -= 1
return current
This has time complexity O(log n). This is because we are halving the number of operations each time we count a prefix.
This has space complexity O(1) as we do not store extra data structures in memory.
The full solution can be found near the top of the page.