算法编程题-最大为N的数字组合

算法编程题-最大为N的数字组合

原题描述

leetcode 902 最大为N的数字组合:给定一个字符串数组,只包含0-9范围内若干数字的字符串形式,以及一个target,求出所有小于等于target的且只能用字符串数组的数字组成的数字的数量。

思路简述

这个题目和之前介绍的一道字节常考的面试题很像,可以从最高位往低位考虑。
首先可以将前缀零考虑一下,因为字符串数组中不包括0,所以0只能出现的数字的开头。假设零只出现在最高位,则剩余n-1(n为target的十进制位数)个位置可以随便取值( m n − 1 m^{n-1} mn1),同里如果零只出现在前两位,则剩余n-2个位置可以随便取值( m n − 2 m^{n-2} mn2),依次类推。
再来考虑非零的情况,从最高位开始遍历。假设遍历到第i位,从字符串数组digits中找到最大的小于等于target该位的数字,如果没找到,说明没有字符串数组里面找不到更小的数字了,此时可以直接返回答案,如果找到了,假设小于target该位的数字的个数为k,则将该位赋值为这些小于的数后,后面的位置可以随便赋值( m n − 1 − i m^{n-1-i} mn1i),如果在字符串数组中能够找到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),使用一个数组来保存目标值的各位数字
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值