题目描述:
给定一个 [ 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 limit−1;其次我们发现最终的输出的位数要么等于 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 n−1个 M M M组成的数字即可需要特别注意的是,如果 M = 0 M=0 M=0那么只需要输出 0 0 0即可,而不是 n − 1 n-1 n−1个 0 0 0。
现在我们需要考虑什么时候输出的位数为 n − 1 n-1 n−1?这时我们就需要先尝试构造 n n n位的数字,如果不能构造 n n n位的数字,那么输出自然就是 n − 1 n-1 n−1位。我们记 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 n−1位的。
需要注意的是,如果遍历到了 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;
}