字节面试题 组成小于某数字的最大数字 构造 贪心

题目描述:

给定一个 [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0,1,2,3,4,5,6,7,8,9]的子集 n u b m e r nubmer nubmer,以及一个不含前导零的正数(该数字可能有很多位),你可以无限次的利用 n u m b e r number number中的元素拼接成某个数字,输出比 l i m i t limit limit小的最大的数字,如果不存在这样的数字,输出空行即可。
例如给定集合 n u m b e r = [ 1 , 3 , 5 ] , l i m i t = 255398 number=[1, 3, 5], limit = 255398 number=[1,3,5],limit=255398那么你应该输出 155555 155555 155555

题解:

首先如果给定的 n u m b e r number number集合包含所有的数字,那么应该输出 l i m i t − 1 limit-1 limit1;其次我们发现最终的输出的位数要么等于 l i m i t limit limit的位数,要么比 l i m i t limit limit的位数少一,我们记 n u m b e r number number中最大的元素为 M M M l i m i t limit limit的位数为 n n n,如果输出的位数比 l i m i t limit limit的位数少一那么我们此时只需要输出 n − 1 n-1 n1 M M M组成的数字即可需要特别注意的是,如果 M = 0 M=0 M=0那么只需要输出 0 0 0即可,而不是 n − 1 n-1 n1 0 0 0
现在我们需要考虑什么时候输出的位数为 n − 1 n-1 n1?这时我们就需要先尝试构造 n n n位的数字,如果不能构造 n n n位的数字,那么输出自然就是 n − 1 n-1 n1位。我们记 n u m b e r number number中的最小的元素为 m m m,用 l i m i t [ i ] limit[i] limit[i]来表示 l i m i t limit limit从左到右的第 i i i个数字;具体的我们进行如下的操作:
1). 我们可以从左到右依次遍历 l i m i t limit limit判断 n u m b e r number number中是否有等于 l i m i t [ i ] limit[i] limit[i]的数字;
2). 如果有,我们此时将输出的第 i i i位记为 l i m i t [ i ] limit[i] limit[i],继续遍历;
3). 如果没有,我们从当前位置向前找到第一个满足: n u m b e r number number中存在一个数字小于当前位置的数的位置,然后将该位置的数更改为 n u m b e r number number中小于该位置数字的最大值,然后后续的位置全部填上 M M M,此时代表存在 n n n位的解;如果没有找到这个位置,那么则解一定是 n − 1 n-1 n1位的。
需要注意的是,如果遍历到了 l i m i t limit limit的最后一位,那么一定需要执行步骤 3).因为题目要求找的是小于 l i m i t limit limit的数字。
例如 n u m b e r = [ 1 , 2 , 3 , 5 ] , l i m i t = 2553980 number = [1, 2, 3, 5], limit = 2553980 number=[1,2,3,5],limit=2553980那么依次遍历会填入 2553 2553 2553之后,发现不存在 9 9 9然后从当前位置向前找到的第一个位置就是当前位置,那么填入 5 5 5之后变成 25535 25535 25535再将后续的数字填上最大值 5 5 5所以最终的输出是 255355 255355 255355
上述的过程由于在填的过程中前面的数字一定是与 l i m i t limit limit前面的数字是一样的,当出现第一个小于的时候,那么后续就可以全部用最大值,这样得到的数字也是最大的。

代码:

#include <bits/stdc++.h>

using namespace std;

int m, number;
set<int> numberSet;
string limit;

int main()
{
    ios::sync_with_stdio(false);
    cin >> m;
    for (int i = 0; i < m; i++) {
        cin >> number;
        numberSet.insert(number);
    }
    cin >> limit;
    for (int i = 0; i < limit.length(); i++) {
        if (i == limit.length() - 1 || !numberSet.count(limit[i] - '0')) {
            while (i >= 0) {
                auto iter = numberSet.lower_bound(limit[i] - '0');
                if (iter != numberSet.begin()) {
                    iter--; // 减1到小于limit[i]最大的数字
                    cout << limit.substr(0, i) << *iter;
                    for (int j = i + 1; j < limit.length(); j++) {
                        cout << *numberSet.rbegin();
                    }
                    break;
                }
                i--;
            }
            if (i < 0) {
                for (int j = 0; j < limit.length() - 1; j++) {
                    cout << *numberSet.rbegin();
                }
            }
            cout << endl;
            break;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值