链接直达

1. 题目描述

给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。例如,如果 digits = ['1','3','5'],我们可以写数字,如 '13''551', 和 '1351315'

返回 可以生成的小于或等于给定整数 n 的正整数的个数 。

示例 1:

输入:digits = ["1","3","5","7"], n = 100

输出:20

解释:

可写出的 20 个数字是:

1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

示例 2:

输入:digits = ["1","4","9"], n = 1000000000

输出:29523

解释:

我们可以写 3 个一位数字,9 个两位数字,27 个三位数字,

81 个四位数字,243 个五位数字,729 个六位数字,

2187 个七位数字,6561 个八位数字和 19683 个九位数字。

总共,可以使用D中的数字写出 29523 个整数。

示例 3:

输入:digits = ["7"], n = 8

输出:1

提示:

  • 1 <= digits.length <= 9

  • digits[i].length == 1

  • digits[i] 是从 '1' 到 '9' 的数

  • digits 中的所有值都 不同

  • digits 按 非递减顺序 排列

  • 1 <= n <= 109

2. 分析

核心思路:枚举分类讨论

分类讨论:

假设n有x位

答案:

1. 1~n-1 位时,每一位可以选择所有元素

1: len(digit)

2: len(digit)^2

3: len(digit)^3

sum(len(digit)^x) 1<=x<=n-1

2. n位时:

递归求解每一位:

小于最高位有x个,x+剩余位长度^x-1

3. 代码

func atMostNGivenDigitSet(digits []string, n int) int {
	nArr := []int{}
	for n > 0 {
		nArr = append([]int{n % 10}, nArr...)
		n /= 10
	}
	digit := make([]int, len(digits))
	for i, d := range digits {
		digit[i], _ = strconv.Atoi(d)
	}
	sort.Ints(digit)
	ret := 0
	// 先计算低位 1-n-1
	for i := 1; i < len(nArr); i++ {
		ret += int(math.Pow(float64(len(digit)), float64(i)))
	}
	// 计算答案一共有n位
	// 从高位依次开始迭代每一位元素
	for i := 0; i < len(nArr); i++ {
		cur := nArr[i]
		// digit[idx] > cur digit[idx-1] <= cur
		idx := sort.Search(len(digit), func(i int) bool {
			return digit[i] > cur
		})
		// 当前位找不到满足元素
		if idx == 0 {
			break
		}
		// 更新后面的所有答案
		if digit[idx-1] < cur {
			remain := (len(nArr) - i - 1)
			ret += idx * int(math.Pow(float64(len(digit)), float64(remain)))
			break
		}
		if digit[idx-1] == cur {
			if idx-1 > 0 {
				remain := len(nArr) - i - 1
				ret += (idx - 1) * int(math.Pow(float64(len(digit)), float64(remain)))
			}
			// 相等看低位 如果是最后一位,则整体结果加一(所有位正好找到相等的),结束
			if i == len(nArr)-1 {
				ret++
				break
			}
			continue
		}
	}
	return ret
}