Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 26b007e

Browse files
feat: add solutions to lc problem: No.0440 (doocs#4469)
No.0440.K-th Smallest in Lexicographical Order
1 parent f5db36b commit 26b007e

File tree

4 files changed

+268
-2
lines changed

4 files changed

+268
-2
lines changed

‎solution/0400-0499/0440.K-th Smallest in Lexicographical Order/README.md‎

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,37 @@ tags:
4949

5050
<!-- solution:start -->
5151

52-
### 方法一
52+
### 方法一:字典树计数 + 贪心构造
53+
54+
本题要求在区间 $[1, n]$ 中,按**字典序**排序后,找到第 $k$ 小的数字。由于 $n$ 的范围非常大(最多可达 10ドル^9$),我们无法直接枚举所有数字后排序。因此我们采用**贪心 + 字典树模拟**的策略。
55+
56+
我们将 $[1, n]$ 看作一棵 **十叉字典树(Trie)**:
57+
58+
- 每个节点是一个前缀,根节点为空串;
59+
- 节点的子节点是当前前缀拼接上 0ドル \sim 9$;
60+
- 例如前缀 1ドル$ 会有子节点 10,ドル 11, \ldots, 19,ドル而 10ドル$ 会有 100,ドル 101, \ldots, 109$;
61+
- 这种结构天然符合字典序遍历。
62+
63+
```
64+
65+
├── 1
66+
│ ├── 10
67+
│ ├── 11
68+
│ ├── ...
69+
├── 2
70+
├── ...
71+
```
72+
73+
我们使用变量 $\textit{curr}$ 表示当前前缀,初始为 1ドル$。每次我们尝试向下扩展前缀,直到找到第 $k$ 小的数字。
74+
75+
每次我们计算当前前缀下有多少个合法数字(即以 $\textit{curr}$ 为前缀、且不超过 $n$ 的整数个数),记作 $\textit{count}(\text{curr})$:
76+
77+
- 如果 $k \ge \text{count}(\text{curr})$:说明目标不在这棵子树中,跳过整棵子树,前缀右移:$\textit{curr} \leftarrow \text{curr} + 1,ドル并更新 $k \leftarrow k - \text{count}(\text{curr})$;
78+
- 否则:说明目标在当前前缀的子树中,进入下一层:$\textit{curr} \leftarrow \text{curr} \times 10,ドル并消耗一个前缀:$k \leftarrow k - 1$。
79+
80+
每一层我们将当前区间扩大 10ドル$ 倍,向下延伸到更长的前缀,直到超出 $n$。
81+
82+
时间复杂度 $O(\log^2 n),ドル空间复杂度 $O(1)$。
5383

5484
<!-- tabs:start -->
5585

@@ -181,6 +211,75 @@ func findKthNumber(n int, k int) int {
181211
}
182212
```
183213

214+
#### TypeScript
215+
216+
```ts
217+
function findKthNumber(n: number, k: number): number {
218+
function count(curr: number): number {
219+
let next = curr + 1;
220+
let cnt = 0;
221+
while (curr <= n) {
222+
cnt += Math.min(n - curr + 1, next - curr);
223+
curr *= 10;
224+
next *= 10;
225+
}
226+
return cnt;
227+
}
228+
229+
let curr = 1;
230+
k--;
231+
232+
while (k > 0) {
233+
const cnt = count(curr);
234+
if (k >= cnt) {
235+
k -= cnt;
236+
curr += 1;
237+
} else {
238+
k -= 1;
239+
curr *= 10;
240+
}
241+
}
242+
243+
return curr;
244+
}
245+
```
246+
247+
#### Rust
248+
249+
```rust
250+
impl Solution {
251+
pub fn find_kth_number(n: i32, k: i32) -> i32 {
252+
fn count(mut curr: i64, n: i32) -> i32 {
253+
let mut next = curr + 1;
254+
let mut total = 0;
255+
let n = n as i64;
256+
while curr <= n {
257+
total += std::cmp::min(n - curr + 1, next - curr);
258+
curr *= 10;
259+
next *= 10;
260+
}
261+
total as i32
262+
}
263+
264+
let mut curr = 1;
265+
let mut k = k - 1;
266+
267+
while k > 0 {
268+
let cnt = count(curr as i64, n);
269+
if k >= cnt {
270+
k -= cnt;
271+
curr += 1;
272+
} else {
273+
k -= 1;
274+
curr *= 10;
275+
}
276+
}
277+
278+
curr
279+
}
280+
}
281+
```
282+
184283
<!-- tabs:end -->
185284

186285
<!-- solution:end -->

‎solution/0400-0499/0440.K-th Smallest in Lexicographical Order/README_EN.md‎

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,46 @@ tags:
4747

4848
<!-- solution:start -->
4949

50-
### Solution 1
50+
### Solution 1: Trie-Based Counting + Greedy Construction
51+
52+
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**.
53+
54+
We treat the range $[1, n]$ as a **10-ary prefix tree (Trie)**:
55+
56+
- Each node represents a numeric prefix, starting from an empty root;
57+
- Each node has 10 children, corresponding to appending digits 0ドル \sim 9$;
58+
- For example, prefix 1ドル$ has children 10,ドル 11, \ldots, 19,ドル and node 10ドル$ has children 100,ドル 101, \ldots, 109$;
59+
- This tree naturally reflects lexicographical order traversal.
60+
61+
```
62+
root
63+
├── 1
64+
│ ├── 10
65+
│ ├── 11
66+
│ ├── ...
67+
├── 2
68+
├── ...
69+
```
70+
71+
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.
72+
73+
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})$:
74+
75+
- 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:
76+
77+
$$
78+
\textit{curr} \leftarrow \textit{curr} + 1,\quad k \leftarrow k - \text{count}(\text{curr})
79+
$$
80+
81+
- Otherwise: the target is within this subtree. We go one level deeper:
82+
83+
$$
84+
\textit{curr} \leftarrow \textit{curr} \times 10,\quad k \leftarrow k - 1
85+
$$
86+
87+
At each level, we enlarge the current range by multiplying by 10 and continue descending until we exceed $n$.
88+
89+
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.
5190

5291
<!-- tabs:start -->
5392

@@ -179,6 +218,75 @@ func findKthNumber(n int, k int) int {
179218
}
180219
```
181220

221+
#### TypeScript
222+
223+
```ts
224+
function findKthNumber(n: number, k: number): number {
225+
function count(curr: number): number {
226+
let next = curr + 1;
227+
let cnt = 0;
228+
while (curr <= n) {
229+
cnt += Math.min(n - curr + 1, next - curr);
230+
curr *= 10;
231+
next *= 10;
232+
}
233+
return cnt;
234+
}
235+
236+
let curr = 1;
237+
k--;
238+
239+
while (k > 0) {
240+
const cnt = count(curr);
241+
if (k >= cnt) {
242+
k -= cnt;
243+
curr += 1;
244+
} else {
245+
k -= 1;
246+
curr *= 10;
247+
}
248+
}
249+
250+
return curr;
251+
}
252+
```
253+
254+
#### Rust
255+
256+
```rust
257+
impl Solution {
258+
pub fn find_kth_number(n: i32, k: i32) -> i32 {
259+
fn count(mut curr: i64, n: i32) -> i32 {
260+
let mut next = curr + 1;
261+
let mut total = 0;
262+
let n = n as i64;
263+
while curr <= n {
264+
total += std::cmp::min(n - curr + 1, next - curr);
265+
curr *= 10;
266+
next *= 10;
267+
}
268+
total as i32
269+
}
270+
271+
let mut curr = 1;
272+
let mut k = k - 1;
273+
274+
while k > 0 {
275+
let cnt = count(curr as i64, n);
276+
if k >= cnt {
277+
k -= cnt;
278+
curr += 1;
279+
} else {
280+
k -= 1;
281+
curr *= 10;
282+
}
283+
}
284+
285+
curr
286+
}
287+
}
288+
```
289+
182290
<!-- tabs:end -->
183291

184292
<!-- solution:end -->
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
impl Solution {
2+
pub fn find_kth_number(n: i32, k: i32) -> i32 {
3+
fn count(mut curr: i64, n: i32) -> i32 {
4+
let mut next = curr + 1;
5+
let mut total = 0;
6+
let n = n as i64;
7+
while curr <= n {
8+
total += std::cmp::min(n - curr + 1, next - curr);
9+
curr *= 10;
10+
next *= 10;
11+
}
12+
total as i32
13+
}
14+
15+
let mut curr = 1;
16+
let mut k = k - 1;
17+
18+
while k > 0 {
19+
let cnt = count(curr as i64, n);
20+
if k >= cnt {
21+
k -= cnt;
22+
curr += 1;
23+
} else {
24+
k -= 1;
25+
curr *= 10;
26+
}
27+
}
28+
29+
curr
30+
}
31+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
function findKthNumber(n: number, k: number): number {
2+
function count(curr: number): number {
3+
let next = curr + 1;
4+
let cnt = 0;
5+
while (curr <= n) {
6+
cnt += Math.min(n - curr + 1, next - curr);
7+
curr *= 10;
8+
next *= 10;
9+
}
10+
return cnt;
11+
}
12+
13+
let curr = 1;
14+
k--;
15+
16+
while (k > 0) {
17+
const cnt = count(curr);
18+
if (k >= cnt) {
19+
k -= cnt;
20+
curr += 1;
21+
} else {
22+
k -= 1;
23+
curr *= 10;
24+
}
25+
}
26+
27+
return curr;
28+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /