K-th Smallest in Lexicographical Order

Updated on 09 June, 2025
K-th Smallest in Lexicographical Order header image

Problem Statement

In this problem, you are provided with two integers, n and k. You are required to determine the kth lexicographically smallest integer within the inclusive range from 1 to n. Lexicographically order treats numbers as though they are words in a dictionary where digits are compared like characters. For instance, 11 comes before 2 because 1 is less than 2 when viewed as a string, even though numerically, 2 is smaller than 11.

Examples

Example 1

Input:

n = 13, k = 2

Output:

10

Explanation:

The lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], so the second smallest number is 10.

Example 2

Input:

n = 1, k = 1

Output:

1

Constraints

  • 1 <= k <= n <= 109

Approach and Intuition

To find the kth lexicographically smallest integer in a given range [1, n], our approach revolves around understanding the lexicographical order and efficiently navigating through numbers to find our desired position without generating the entire sequence. This requires both intuition about string comparison and strategic counting:

  1. First, understand the nature of lexicographical order vs numerical order:

    • Lexicographical order means the numbers are ordered as if they were strings or words.
    • Thus, '10' is less than '2' because '1' comes before '2' when viewing the characters from left to right.
  2. Based on the example calculations:

    • In Example 1, with n = 13, the sequence in lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9]. When examining this order, we notice that all numbers starting with '1' appear before those starting with '2'. Here, the second smallest number is '10'.
    • In Example 2, with n = 1, the sequence is simply [1], as it's the only number in the range.
  3. Therefore, a step-by-step breakdown can be:

    • Start with the lowest number (1).
    • Incrementally explore the next possible number sequences by considering numbers as strings — append the next possible digit and check whether this still falls within the range and meets the kth requirement.
    • For large values of n, optimizing the search using a tree or graph traversal can significantly reduce redundant checks. This might involve skipping over large sections of the number space that fall lexicographically beyond our kth value.

Through understanding lexicographical ordering and applying methods of efficient searching and skipping of numbers, this problem, despite its simplicity in constraints, poses an interesting challenge in the design of algorithms to meet the requirements of both correctness and efficiency.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    int findKthSmallest(int n, int k) {
        int current = 1;
        k--;

        while (k > 0) {
            int steps = calculateSteps(n, current, current + 1);
            if (steps <= k) {
                current++;
                k -= steps;
            } else {
                current *= 10;
                k--;
            }
        }

        return current;
    }

private:
    int calculateSteps(int n, long start, long next) {
        int steps = 0;
        while (start <= n) {
            steps += min((long)(n + 1), next) - start;
            start *= 10;
            next *= 10;
        }
        return steps;
    }
};

This program, written in C++, focuses on finding the k-th smallest number in lexicographical order for numbers from 1 to n. Begin by initializing the current value as 1 and adjusting k by decrementing it by one. Utilize a loop to determine the necessary steps to move closer to the k-th smallest number by leveraging a calculateSteps helper function. This function computes the step difference between numbers at positions dictated by start and next within the bounds up to n. If the step count moves beyond or meets k, multiply the current number by 10, else increment the current number and reduce k by the obtained steps. The loop runs until k becomes zero, where current holds the k-th smallest number in lexicographical order. This efficient approach avoids sorting and provides a direct path to the solution utilizing the properties of numbers in lexicographic sequence.

java
class Solution {

    public int locateKthNumber(int maxNumber, int kthPosition) {
        int current = 1;
        kthPosition--;

        while (kthPosition > 0) {
            int steps = calculateSteps(maxNumber, current, current + 1);
            if (steps <= kthPosition) {
                current++;
                kthPosition -= steps;
            } else {
                current *= 10;
                kthPosition--;
            }
        }

        return current;
    }

    private int calculateSteps(int maxNumber, long startPrefix, long endPrefix) {
        int stepsCount = 0;
        while (startPrefix <= maxNumber) {
            stepsCount += Math.min(maxNumber + 1, endPrefix) - startPrefix;
            startPrefix *= 10;
            endPrefix *= 10;
        }
        return stepsCount;
    }
}

The provided Java solution is designed to find the k-th smallest number in lexicographical order given a maximum number limit. Here is a concise summary of how the solution works:

  • Initialization:

    • The function locateKthNumber initializes current to 1 and decrements kthPosition by 1 to adjust for zero-based indexing.
  • Main Loop:

    • A loop continues until kthPosition becomes zero.
    • Inside the loop, the method calculateSteps calculates the number of lexicographical steps between two prefixes by:
      • Iterating from the startPrefix to maxNumber.
      • Incrementing the prefix steps within the given range.
    • Depending on the comparison between steps and kthPosition:
      • If steps is less than or equal to kthPosition, it moves to the next prefix by incrementing current and decrementing kthPosition by the number of steps.
      • Otherwise, it delves deeper into the lexicographical tree by multiplying current by 10 and decrementing kthPosition by 1.
  • Result:

    • Once the loop exits, current holds the k-th smallest number in a lexicographical sequence up to maxNumber.

This approach efficiently navigates through the numbers in lexicographical order, leveraging a hierarchical step calculation to jump through ranges or dive deeper as needed. Repeated calculations are avoided through efficient prefix manipulation and range calculation, ensuring optimal performance in locating the desired smallest element.

python
class Solution(object):
    def findKthNumber(self, n, k):
        current = 1
        k -= 1

        while k > 0:
            steps = self._calculate_steps(n, current, current + 1)
            if steps <= k:
                current += 1
                k -= steps
            else:
                current *= 10
                k -= 1

        return current

    def _calculate_steps(self, n, prefix1, prefix2):
        total_steps = 0
        while prefix1 <= n:
            total_steps += min(n + 1, prefix2) - prefix1
            prefix1 *= 10
            prefix2 *= 10
        return total_steps

This solution is designed to find the k-th smallest number in lexicographical order up to n. The provided Python solution efficiently tackles the problem using a series of calculated steps to navigate through the numbers in lexicographical sequence until the k-th element is found. The main logic resides within the findKthNumber function of the Solution class, which manipulates the current number and decreases k until it finds the desired k-th smallest number.

  • Every number sequence starts with the current value initialized to 1 and decreases k initially by 1 since the count starts from the current number itself.
  • A loop iterates until k becomes zero. Inside this loop, the _calculate_steps helper function computes the number of possible steps from the current number to the next in lexicographical order.
  • If the computed steps from the current node to the next is less than or equal to k, this indicates not reaching the k-th number yet. Thus, move to the next initial number in the sequence by incrementing the current number and decreasing k by the number of steps just taken.
  • Otherwise, dive deeper into the lexicographical tree by multiplying the current number by 10 and decrementing k, which effectively moves to the next level in the tree structure of numbers.

The _calculate_steps function aids in finding the distance between two prefixes by counting all possible numbers that can be formed starting with a given prefix within the limit n. It adjusts the prefix1 and prefix2 to higher powers of 10 in each iteration, summing up the counts until prefix1 surpasses n.

Using this algorithm ensures that the solution is not only correct but also optimized for scenarios involving large values of n, thereby reducing unnecessary computations. The approach leverages numerical properties and the structure of lexicographical ordering to achieve efficiency.

Comments

No comments yet.