最大数字
原创
©著作权归作者所有:来自51CTO博客作者隙间光点的原创作品,请联系作者获取转载授权,否则将追究法律责任
题目描述
- 给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现
2
次,超过的需要进行删除; - 删除某个重复的数字后,其它数字相对位置保持不变如
“34533”
- 数字
3
重复超过2
次,需要删除其中一个3
,删除第一个3
后获得最大数值“4533”
- 请返回经过删除操作后的最大的数值,以字符串表示
输入描述
- 第一行为一个纯数字组成的字符串,长度范围:
[1, 100 000]
输出描述
用例
用例1
用例2
--输入
5445795045
--输出
5479504
题目解析
错误的思考过程
- 一个容易观察到的点是:在经过删除重复数字的操作之后,得到的字符串的长度是固定了
- 那么就需要找到一个通用的删除规则
ex: 219222111
很显然,这个例子用上帝视角观察的话,删除之后能够得到的最大的数字是 92211
似乎从后往前删除更靠谱一些
ex: 34533
以这个例子而言,最优的结果是删除 第一个 3 最终得到 4533
似乎从后往前又不太靠谱了
- 要想得到最大的数字,那么肯定要让 “第一位最大”
- 那么好,似乎有了一个方法
ex:
219222111
34533
对于 219222111 从头开始遍历
2 出现的次数是 4,满足可以删除的条件,那么向后找,找有没有比 2 大的且可以保留的数字,那么这样的话,开头是 1 的话,肯定可以删除。
2 向后走,遇到了 1,这里归属于小于当前数字,如果 1 不满足数字移除条件,那么 当前数字 2 就不能移除。
反之,继续向后找。如果找到了,那么当前数字可以删除,如果找不到,那么当前数字不能移除
这样子,从前往后遍历,遍历到的位置均认为自己是 数字的开始位置。
- 继续总结一下整体的过程
- 首先遍历一遍字符串,记录每一个数字出现的次数
- 然后从字符串的第一个字符开始遍历。拿到其出现的次数。那么现在考虑判断的顺序。
- 首先判断其下一个数字是否和当前数字相同,如果相同,且出现次数大于2,则当前数字可以被删除.
- 如果下一个数字和当前数字不相同,这里可能存在特殊情况。
- 如果当前数字是 9,那么一定不可以被移除。但是存在一种例外情况,啧啧啧。写到这里这个思路似乎是错误的
- 因为没办法处理
818181818181
这种情况
新的思考过程
- 从第一个字符开始遍历,那么假如仅仅只有一个字符的情况下的话,那么第一个字符肯定会被保留
- 那么遍历到第二个字符,第二个字符出现了可以和第一个字符做比较,来决定第一个字符是否可以保留
- 依照这个思路
- 可以首先遍历一次字符串,记录下每一个字符数字出现的次数
- 然后记录一下保留的 字符数字 的个数
- 当遍历到一个新的字符位置。
- 让其和已经保留的字符进行相比较,如果当前字符比已经保留的前一个字符大,那么可以继续考虑是否可以移除前一个字符。
- 这里如何判断呢?可以利用 保留的字符个数 和 目前等待使用的字符数字的个数
show code
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextLine()) {
String line = in.nextLine();
maxNumber(line);
}
}
private static void maxNumber(String num) {
int n = num.length();
// 统计每一个 字符数字 出现的次数
Map<Character, Integer> sts = new HashMap<>();
// 统计已经保留的 字符数字 的个数
Map<Character, Integer> keep = new HashMap<>();
// 用一个队列保留已经保存的 字符数字
LinkedList<Character> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
char at = num.charAt(i);
sts.put(at, sts.getOrDefault(at, 0) + 1);
// 初始化一个 字符数字 都不保留
keep.putIfAbsent(at, 0);
}
//开始遍历 字符串
for (int i = 0; i < n; i++) {
char at = num.charAt(i);
// 这里还需要首先判断一下已经保留的字符数字个数有没有两个,如果有,那么跳过
if(keep.get(at) == 2) {
// 表示当前的字符可以被移除了,对应的 可用的 字符数字 个数减1
sts.put(at, sts.get(at) - 1);
continue;
}
// 如果队列不为空
while(!queue.isEmpty()) {
// 取出队列最后入队的一个字符
Character lastInQueue = queue.getLast();
// 判断 最后入队的字符是否可以移除
// 第二个判断是:判断移除之后,保留的字符个数和剩余的字符个数的总数量是否够 2 个
// 两个判断条件都满足了,则 最后入队的字符是否可以移除
if(at > lastInQueue && sts.get(lastInQueue) + keep.get(lastInQueue) - 1 >= 2) {
// 删除之后的操作
queue.removeLast();
// 对应的保留字符的个数减少1
keep.put(lastInQueue, keep.get(lastInQueue) - 1);
} else {
// 不满足条件,直接跳过
break;
}
}
// 这里保留当前字符
queue.addLast(at);
// 可使用的 字符数字个数 减少1
sts.put(at, sts.get(at) - 1);
// 保留的 字符数字的个数 增加 1
keep.put(at, keep.get(at) + 1);
}
// 输出结果
StringBuilder ans = new StringBuilder();
for (Character at : queue) {
ans.append(at);
}
System.out.println(ans);
}