原题描述
leetcode 902 最大为N的数字组合:给定一个字符串数组,只包含0-9范围内若干数字的字符串形式,以及一个target,求出所有小于等于target的且只能用字符串数组的数字组成的数字的数量。
思路简述
这个题目和之前介绍的一道字节常考的面试题很像,可以从最高位往低位考虑。
首先可以将前缀零考虑一下,因为字符串数组中不包括0,所以0只能出现的数字的开头。假设零只出现在最高位,则剩余n-1(n为target的十进制位数)个位置可以随便取值(
m
n
−
1
m^{n-1}
mn−1),同里如果零只出现在前两位,则剩余n-2个位置可以随便取值(
m
n
−
2
m^{n-2}
mn−2),依次类推。
再来考虑非零的情况,从最高位开始遍历。假设遍历到第i位,从字符串数组digits中找到最大的小于等于target该位的数字,如果没找到,说明没有字符串数组里面找不到更小的数字了,此时可以直接返回答案,如果找到了,假设小于target该位的数字的个数为k,则将该位赋值为这些小于的数后,后面的位置可以随便赋值(
m
n
−
1
−
i
m^{n-1-i}
mn−1−i),如果在字符串数组中能够找到target当前位一样的数字,则需要继续遍历,但要注意边界条件,即最后一位也能找到相等的,意味着digits中的数字可以组装成一个和target相同的数字。
笔者看过其他人的题解,一种更加标准的思路是数位dp,但实际上,具体的实现思路和这个也是类似的。
代码实现
func atMostNGivenDigitSet(strs []string, target int) int {
// 预处理
numbers := getNumbers(target)
digits := make([]int, len(strs))
for i, str := range strs {
digits[i], _ = strconv.Atoi(str)
}
sort.Slice(digits, func(i, j int) bool {
return digits[i] < digits[j]
})
n := len(numbers)
m := len(digits)
ans := 0
for i := 1; i < n; i++ {
ans += Pow(m, i)
}
// 采用字典树的思想
for i := 0; i < n; i++ {
pos := getMaxSmaller(digits, numbers[i])
if pos == -1 {
return ans // 往后找不到了
}
t := Pow(m, n - 1 - i)
ans += max(0, pos) * t
if digits[pos] < numbers[i] || i == n - 1 {
return ans + t
}
}
return ans
}
// Pow 基于快速幂实现a^b, a, b都必须为正整数且程序不会考虑结果的溢出
func Pow(a, b int) int {
res := 1
for b > 0 {
if (b & 1) > 0 {
res *= a
}
a *= a
b >>= 1
}
return res
}
// getNumbers 返回num的各位数字
func getNumbers(num int) []int {
if num == 0 {
return []int{0}
}
numbers := make([]int, 12)
k := 11
for num > 0 {
numbers[k] = num % 10
k--
num /= 10
}
return numbers[k + 1:]
}
// getMaxSmaller 返回有序数组nums中小于等于target的最大整数
func getMaxSmaller(nums []int, target int) int {
low := 0
high := len(nums) - 1
for low < high {
mid := (low + high + 1) / 2
if nums[mid] > target {
high = mid - 1
} else {
low = mid
}
}
if nums[low] > target {
return -1
}
return low
}
在实现中,利用了二分查找去字符串数组中寻找,对于幂次元素也自己使用快速幂进行了计算。

复杂度分析
- 时间复杂度: O ( n l o g m + n l o g n ) O(nlogm+nlogn) O(nlogm+nlogn),其中n为目标值的十进制位数,m为字符串数组的长度,logm来自二分查找,logn来自快速幂。
- 空间复杂度: O ( n ) O(n) O(n),使用一个数组来保存目标值的各位数字
168万+

被折叠的 条评论
为什么被折叠?



