字节跳动高频面试题-从数字集合中构造出小于n的最大数

字节跳动高频面试算法题


1、题目描述

给定集合 A = { x ∈ N   ∣   0 ≤ x ≤ 9 } A = \{x \in \mathbb{N} \,|\, 0 \leq x \leq 9\} A={xN0x9}和一个数字 n n n,请利用集合 A A A中的元素构造出小于 n n n的最大数,集合中的每个元素可以无限使用。
样例 1:A={1, 2, 9, 4},n=2533,返回 2499。
样例 2:A={1, 2, 5, 4},n=2543,返回 2542。
样例 3:A={1, 2, 5, 4},n=2541,返回 2525。
样例 4:A={1, 2, 9, 4},n=2111,返回 1999。
样例 5:A={5, 9},n=5555,返回 999。


二、解题思路

构造策略: 贪心+回溯

  1. 从高位开始遍历,对每一位先尝试使用相同数字,除了最后一位.
  2. 如果没有相同的数字时,尝试是否有比当前数字更小的
  3. 1). 有的话选更小的数字里最大的,剩下的用最大数字。
    2). 都没有就向前回溯看前一个有没有更小的。如果一直回溯到第一个数字都没有更小的数字,就用位数更少的全都是最大数字的数。
  4. 处理一些特殊情况:
    ①能够完全构造出n——>把问题转换为构造n-1。
    ②n的最高位数都比集合A中的所有数小,那么构造出的位数会比n少一位。——>每一位都填A中最大的数。
    ③特殊判断一下回溯到最高位时,出现前导0的情况。——>和情况②类似。

三、Python代码实现

# 这道题更多的是在操作字符串
# 示例 1:A={1, 2, 9, 4},n=2533,返回 2499。
def find_max_number(A, n):
    A.sort(reverse=True)
    n_str = str(n)

    # 特判一种情况, 如果n中的每一个数字都在A集合里,那么我去构造n-1才行. 因为题目要求是严格小于,而不是小于等于
    flag_in = True
    for d in n_str:
        if int(d) not in A:
            flag_in = False
            break
    if flag_in:
        n -= 1
        n_str = str(n)    
    
    # 预处理10个数(0-9),从集合A中获取小于等于给定数字d的最大数字,并存在mp里
    mp = {}
    for i in range(10):
        flag = False # 记录是否找到
        for j in A:
            if j <= i:
                mp[str(i)] = j # 第一个小于等于i的数字是集合A中的j
                flag = True
                break
        if not flag:
            mp[str(i)] = -1 # 说明没有小于等于i的数字

    ans = ""
    flag_eql = False # 记录后续是否需要全部取A集合的最大值
    flag_back = False # 记录当前是否处于回溯状态
    idx = 0
    while idx < len(n_str) and idx >= 0:
        if flag_eql:
            ans += str(A[0])
            idx += 1
            continue

        d = n_str[idx] if not flag_back else str(int(n_str[idx]) - 1)

        if idx == 0 and mp[d] == 0:
            idx -= 1
            break
        
        if mp[d] != -1:
            if not flag_back:
                if mp[d] == int(d): # 有相同数字
                    pass
                else: # 没有相同的数字, 但是存在比d小的数, 那么后续全部取最大值即可
                    flag_eql = True
            else:
                flag_back = False
                flag_eql = True
            ans += str(mp[d]) # 从mp[d]中构造一位新的数字填入
            idx += 1
        else: # 需要向前回溯一位
            idx -= 1
            ans = ans[:-1]
            flag_back = True        

    if idx < 0:
        ans = str(A[0]) * (len(n_str) - 1)
        
    return ans

test_cases_corrected = [
    ([1, 2, 9, 4], 2533),
    ([1, 2, 5, 4], 2543),
    ([1, 2, 5, 4], 2541),
    ([1, 2, 9, 4], 2111),
    ([5, 9], 5555),
    ([2,4,5],24131),
    ([0,7,8],312),
    ([1,3,5,7],2410)
]

results_corrected = [find_max_number(A, n) for A, n in test_cases_corrected]
print(results_corrected)

四、参考链接

参考博客链接:小于 n 的最大数

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值