【备战秋招】每日一题:2023.04.14-华为笔试(第三题)-最大数字

为了更好的阅读体检,可以查看我的算法学习网
在线评测链接:P1199

题目内容

塔子哥是一个喜欢收集数字的小朋友,他有一个特殊的相册,里面贴着各种各样的数字,有大有小,有长有短。他每天都会从报纸、杂志、广告牌等地方剪下数字来贴在相册里,他觉得数字很有趣,很美丽。有一天,他的妈妈告诉他,他的相册已经满了,不能再贴新的数字了。塔子哥很难过,他不想放弃任何一个数字,他觉得每个数字都是他的朋友。但是他也知道妈妈说的是对的,他不能再乱贴数字了,否则相册会破掉。

于是,他想到了一个办法,他决定把相册里重复出现的数字删除一些,这样就可以腾出空间来贴新的数字了。但是他又不想删除太多,因为他觉得重复的数字也有它们的魅力。所以他规定了一个原则:字符串中的每个数字最多只能出现 2 次,超过的需要进行删除,删除某个重复的数字后,其它数字相对位置保持不变。

这样做的好处是什么呢?塔子哥想到了一个理由:如果把相册里的所有数字连起来,就可以得到一个很大的数值。如果按照他的原则删除重复的数字,那么得到的数值就会更大。因为删除重复的数字相当于让后面的数字向前移动,这样就可以让更高位上的数字变大。塔子哥觉得这样做很有意思,也很有挑战性。

但是塔子哥发现这个任务并不容易,因为相册里有很多页,每一页都有很多数字。如果要手动删除重复的数字,并且找出最大的数值(可带前导0),那么需要花费很多时间和精力。塔子哥希望能有一个更快捷方便的方法。

于是,他想到了你。你是塔子哥的好朋友,也是一个擅长编程和算法的高手。塔子哥知道你可以用计算机来帮助他解决这个问题。所以他把相册里所有页上的数字都拍成了照片,并且用字符串表示了每一页上的数值。然后他把这些字符串发给了你,并且请你帮忙得到经过删除操作后的最大的数值,以字符串表示。

输入描述

第一行为一个纯数字组成的字符串 s t r str str 。( 1 ≤ s t r . l e n g t h ( ) ≤ 100000 1\le str.length() \le 100000 1str.length()100000

可能携带前导0

输出描述

输出经过删除操作后的最大的数值。允许存在前导0

样例

样例1

输入

22232

输出

232

样例2

输入

331321131323300

输出

33211200

单调递减(有限递减)栈+哈希表维护数量

import java.util.*;
class Main{
    public static void main(String[] args){
        // 最大字典序问题,重复数字保留至多两位,单调递减栈 + 哈希表
        // 题目要求
        // 1、给定的s串中每个数字至多出现两次(只能删除重复两次以上的部分)
        // 2、数字类型不能缺失(不同的数字数量必须保持一致)
        // 3、各个数字前后次序关系不能乱,也就是必须原地删除
        // 4、所得最终结果表示为数字时最大,可以理解为字典序最大,也就是让越大的数字尽量保留在靠前的位置。
        // 并且数字位数要在合理范围内最大(有效位数越多数字越大),那么也就是任何重复数字都保留最大可存在次数2,不重复数字保留1个。
        Scanner input = new Scanner(System.in);
        String s = input.next();
        int n = s.length();
        // hash维护剩余未遍历字符串中的各个数字数量。
        int[] hash = new int[10];
        // intStack维护栈中已有各个数字数量
        int[] inStack = new int[10];
        // 哈希表统计所有数字出现次数
        for(int i = 0; i < n; ++i) ++hash[s.charAt(i)-'0'];
        Deque<Character> stack = new ArrayDeque<>();
        // 维护单调递减栈,栈中任何时刻所有数字重复次数不超过2,当前遍历的数字c符合栈中的递减排序时直接入栈(c小于等于栈顶数字)
        // 当c大于栈顶数字时,说明将c放在靠前的位置字典序更大,那么需要考虑是否将栈顶弹出,
        // 而弹出栈顶top需要保证后续栈中仍然能凑齐最终结果中应有的top数量,也就是:
        // 1、如果s中top数量>=2,那么结果中必须有两个top;
        // 2、如果s中只有一个top,那结果中也必须有一个top。
        // 而所有已经弹出的数字都视为舍弃不用,那么栈顶数字能弹出的条件就是栈中已存在的top数量+后方未遍历部分剩余的top数量>2。如果只有一个必定不可弹出否则会缺失数字类型。
        // 即 inStack[top] + hash[top] > 2
        for(int i = 0; i < n; ++i){
            char c = s.charAt(i);
            // 每遍历一个数字,其后方剩余数量hash-1。
            --hash[c - '0'];
            // 若前方栈中已存在两个c,则当前遇到的第三个c直接跳过,因为栈中前方已有的两个c一定在更靠前的位置,对较大字典序的贡献更大(只有数量仅为1或2的数字可能打破栈的递减次序,因为即便遇到更大的数字也无法弹出,否则会造成最终结果数字数量的缺失)。
            if(inStack[c - '0'] == 2) continue;
            while(!stack.isEmpty() && stack.peek() < c && hash[stack.peek()-'0']+inStack[stack.peek()-'0'] > 2){
                --inStack[stack.pop() - '0'];
            }
            // 确定c符合栈的递减次序后将其入栈。
            stack.push(c);
            ++inStack[c-'0'];
        }
        // 全部遍历完s串后,栈中存储的就是在符合不缺失数字类型和数量完整性情况下的最大字典序排列,从栈底依次记录结果即可。
        String res = "";
        while(!stack.isEmpty()){
            res += stack.pollLast();
        }
        System.out.println(res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值