-
Notifications
You must be signed in to change notification settings - Fork 128
/
Copy pathnumbers_with_repeated_digits.go
72 lines (66 loc) · 1.84 KB
/
numbers_with_repeated_digits.go
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package problem1012
func numDupDigitsAtMostN(N int) int {
digits := convert(N + 1) // +1 为为了把判别条件从 <= 简化成 <
bits := len(digits)
noRepeat := 0
// 统计宽度短于 N 的不含重复数字的数的个数
for b := 1; b < bits; b++ {
noRepeat += 9 * count(9, b-1)
}
// 统计和 N 具有相同高位的且不含重复数字的数的个数
hasSeen := make(map[int]bool, 10)
for b := 0; b < bits; b++ {
d := 0
if b == 0 {
d = 1 // 最高位上的数字不能为 0
}
for ; d < digits[b]; d++ {
// 如果 hasSeen[d]=true
// 说明在更高位上已经出现过 d 了
// 无论低位上再出现什么,都不是 noRepeat 了
if !hasSeen[d] {
noRepeat += count(9-b, bits-b-1)
}
}
if hasSeen[digits[b]] {
// digits[:b+1] 中出现了重复的数字
// 不可能再有 noRepeat 了
break
}
hasSeen[digits[b]] = true
}
return N - noRepeat
}
func convert(N int) []int {
res := make([]int, 0, 10)
for N > 0 {
res = append(res, N%10)
N /= 10
}
swap(res)
return res
}
func swap(A []int) {
i, j := 0, len(A)-1
for i < j {
A[i], A[j] = A[j], A[i]
i++
j--
}
}
// 统计了 m 个数字组成宽度为 n 的数值时,
// 不含重复数字的数值的个数。
// 注意,
// 真正使用 count 的时候,是为了统计 n+1 位宽度的数值
// 在 n+1 位的最高位上不放置 0
// 就可以保证剩下的 n 位的宽度确实是 n
// 所以,count 在使用时,还需要乘上 n+1 位上可供选择的数字的个数
// 例如, 9*count(9, i-1) 的实际含义才是,所有宽度为 i 且不包含重复数字的数值个数
// 例如, count(8,i) 的实际含义是,以 X 开头所有宽度为 i+1 且不包含重复数字的数的个数
// X 可以是任意一个数字
func count(m, n int) int {
if n == 0 {
return 1
}
return count(m, n-1) * (m - n + 1)
}