Skip to content

440. K-th Smallest in Lexicographical Order

Description

Given two integers n and k, return the kth lexicographically smallest integer in the range [1, n].

 

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

Solutions

Solution 1: Trie-Based Counting + Greedy Construction

The problem asks for the $k$-th smallest number in the range \([1, n]\) when all numbers are sorted in lexicographical order. Since \(n\) can be as large as \(10^9\), we cannot afford to generate and sort all the numbers explicitly. Instead, we adopt a strategy based on greedy traversal over a conceptual Trie.

We treat the range \([1, n]\) as a 10-ary prefix tree (Trie):

  • Each node represents a numeric prefix, starting from an empty root;
  • Each node has 10 children, corresponding to appending digits \(0 \sim 9\);
  • For example, prefix \(1\) has children \(10, 11, \ldots, 19\), and node \(10\) has children \(100, 101, \ldots, 109\);
  • This tree naturally reflects lexicographical order traversal.
root
├── 1
│   ├── 10
│   ├── 11
│   ├── ...
├── 2
├── ...

We use a variable \(\textit{curr}\) to denote the current prefix, initialized as \(1\). At each step, we try to expand or skip prefixes until we find the $k$-th smallest number.

At each step, we calculate how many valid numbers (i.e., numbers \(\le n\) with prefix \(\textit{curr}\)) exist under this prefix subtree. Let this count be \(\textit{count}(\text{curr})\):

  • If \(k \ge \text{count}(\text{curr})\): the target number is not in this subtree. We skip the entire subtree by moving to the next sibling:

    \[ \textit{curr} \leftarrow \textit{curr} + 1,\quad k \leftarrow k - \text{count}(\text{curr}) \]
  • Otherwise: the target is within this subtree. We go one level deeper:

    \[ \textit{curr} \leftarrow \textit{curr} \times 10,\quad k \leftarrow k - 1 \]

At each level, we enlarge the current range by multiplying by 10 and continue descending until we exceed \(n\).

The time complexity is \(O(\log^2 n)\), as we perform logarithmic operations for counting and traversing the Trie structure. The space complexity is \(O(1)\) since we only use a few variables to track the current prefix and count.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
    def findKthNumber(self, n: int, k: int) -> int:
        def count(curr):
            next, cnt = curr + 1, 0
            while curr <= n:
                cnt += min(n - curr + 1, next - curr)
                next, curr = next * 10, curr * 10
            return cnt

        curr = 1
        k -= 1
        while k:
            cnt = count(curr)
            if k >= cnt:
                k -= cnt
                curr += 1
            else:
                k -= 1
                curr *= 10
        return curr
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
    private int n;

    public int findKthNumber(int n, int k) {
        this.n = n;
        long curr = 1;
        --k;
        while (k > 0) {
            int cnt = count(curr);
            if (k >= cnt) {
                k -= cnt;
                ++curr;
            } else {
                --k;
                curr *= 10;
            }
        }
        return (int) curr;
    }

    public int count(long curr) {
        long next = curr + 1;
        long cnt = 0;
        while (curr <= n) {
            cnt += Math.min(n - curr + 1, next - curr);
            next *= 10;
            curr *= 10;
        }
        return (int) cnt;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
    int n;

    int findKthNumber(int n, int k) {
        this->n = n;
        --k;
        long long curr = 1;
        while (k) {
            int cnt = count(curr);
            if (k >= cnt) {
                k -= cnt;
                ++curr;
            } else {
                --k;
                curr *= 10;
            }
        }
        return (int) curr;
    }

    int count(long long curr) {
        long long next = curr + 1;
        int cnt = 0;
        while (curr <= n) {
            cnt += min(n - curr + 1, next - curr);
            next *= 10;
            curr *= 10;
        }
        return cnt;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func findKthNumber(n int, k int) int {
    count := func(curr int) int {
        next := curr + 1
        cnt := 0
        for curr <= n {
            cnt += min(n-curr+1, next-curr)
            next *= 10
            curr *= 10
        }
        return cnt
    }
    curr := 1
    k--
    for k > 0 {
        cnt := count(curr)
        if k >= cnt {
            k -= cnt
            curr++
        } else {
            k--
            curr *= 10
        }
    }
    return curr
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function findKthNumber(n: number, k: number): number {
    function count(curr: number): number {
        let next = curr + 1;
        let cnt = 0;
        while (curr <= n) {
            cnt += Math.min(n - curr + 1, next - curr);
            curr *= 10;
            next *= 10;
        }
        return cnt;
    }

    let curr = 1;
    k--;

    while (k > 0) {
        const cnt = count(curr);
        if (k >= cnt) {
            k -= cnt;
            curr += 1;
        } else {
            k -= 1;
            curr *= 10;
        }
    }

    return curr;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
impl Solution {
    pub fn find_kth_number(n: i32, k: i32) -> i32 {
        fn count(mut curr: i64, n: i32) -> i32 {
            let mut next = curr + 1;
            let mut total = 0;
            let n = n as i64;
            while curr <= n {
                total += std::cmp::min(n - curr + 1, next - curr);
                curr *= 10;
                next *= 10;
            }
            total as i32
        }

        let mut curr = 1;
        let mut k = k - 1;

        while k > 0 {
            let cnt = count(curr as i64, n);
            if k >= cnt {
                k -= cnt;
                curr += 1;
            } else {
                k -= 1;
                curr *= 10;
            }
        }

        curr
    }
}

Comments

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy