最大数字问题

一、题目描述

给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现2次,超过的需要进行删除;删除某个重复的数字后,其它数字相对位置保持不变。

如"34533″,数字3重复超过2次,需要删除其中一个3,删除第一个3后获得最大数值"4533″

请返回经过删除操作后的最大的数值,以字符串表示。

二、输入输出描述

输入描述
  • 第一行:纯数字字符串,长度范围 [1, 100000]。
输出描述
  • 删除重复数字后的最大数值字符串(无前置零,空串返回空)。

三、示例

|----|-------|
| 输入 | 34533 |
| 输出 | 4533 |
| 说明 | |

|----|------------|
| 输入 | 5445795045 |
| 输出 | 5479504 |
| 说明 | |

四、解题思路

1. 核心思想

通过 "贪心 + 栈" 策略重构数字字符串:优先让高位保留更大的数字,同时通过unusedreserve两个哈希表保证 "每个数字最终最多保留 2 个" 的约束 ------ 核心是 "栈顶优化(替换更小数字)+ 约束校验(保证数字保留数量)"。

2. 问题本质分析
  • 表层问题:在 "每个数字最多出现 2 次" 的约束下,将原数字字符串重构为尽可能大的新字符串;
  • 深层问题:带约束的贪心排序,核心矛盾是 "追求高位更大" 与 "保证数字出现次数不超 2 次" 的平衡;
  • 关键难点:优化栈顶时,需确保被弹出的数字仍能通过 "剩余未遍历数 + 已保留数" 满足 "最多 2 个" 的约束,避免某数字最终出现次数不足(或超量)。
3. 核心逻辑
  • 约束跟踪:用unused(未遍历的数字数量)和reserve(已保留的数字数量)跟踪每个数字的可用状态,确保最终出现次数≤2;
  • 贪心优化:遍历每个数字时,若当前数字比栈顶大,且弹出栈顶后该数字仍能满足 "保留≤2 个" 的约束,则弹出栈顶(让高位更大);
  • 边界控制:若某数字已保留 2 个,后续该数字直接跳过,不加入栈。
4. 步骤拆解
  1. 状态初始化

    • 统计每个数字的总个数,初始化unused(总个数)和reserve(0);
    • 初始化空栈,用于存储最终结果序列。
  2. 遍历处理每个数字字符

    • 跳过已保留 2 个的数字(直接减少unused,不处理);
    • 循环优化栈顶:若当前数字更大,且弹出栈顶后该数字仍能满足约束,则弹出栈顶;
    • 保留当前数字:入栈,更新unused(-1)和reserve(+1)。
  3. 结果生成

    • 拼接栈中所有字符,得到最终的最大数字字符串。

五、代码实现

java 复制代码
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    String str = sc.next();

    System.out.println(getResult(str));
  }

  public static String getResult(String str) {
    // 每个数字字符的可用个数
    HashMap<Character, Integer> unused = new HashMap<>();
    // 每个数字字符的保留个数
    HashMap<Character, Integer> reserve = new HashMap<>();

    // 初始时,每个数字有多少个,就可用多少个,由于还未使用,因此保留个数为0
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);
      unused.put(c, unused.getOrDefault(c, 0) + 1);
      reserve.putIfAbsent(c, 0);
    }

    // 定义一个栈
    LinkedList<Character> stack = new LinkedList<>();

    // 遍历输入字符串的每个数字字符c
    for (int i = 0; i < str.length(); i++) {
      char c = str.charAt(i);

      // 如果该字符已经保留了2个了,则后续再出现该数字字符可以不保留
      if (reserve.get(c) == 2) {
        // 则可用c数字个数--
        unused.put(c, unused.get(c) - 1);
        continue;
      }

      // 比较当前数字c和栈顶数字top,如果c>top,那么需要考虑将栈顶数字弹出
      while (stack.size() > 0) {
        char top = stack.getLast();

        // 如果栈顶数字被弹出后,已保留的top字符数量和未使用的top字符数量之和大于等于2,则可以弹出,否则不可以
        if (top < c && unused.get(top) + reserve.get(top) - 1 >= 2) {
          stack.removeLast();
          reserve.put(top, reserve.get(top) - 1);
        } else {
          break;
        }
      }

      // 选择保留当前遍历的数字c
      stack.add(c);
      // 则可用c数字个数--
      unused.put(c, unused.get(c) - 1);
      // 已保留c数字个数++
      reserve.put(c, reserve.get(c) + 1);
    }

    StringBuilder sb = new StringBuilder();
    for (Character c : stack) {
      sb.append(c);
    }
    return sb.toString();
  }
}
相关推荐
毕设源码-郭学长3 小时前
【开题答辩全过程】以 高校图书推荐系统的设计与实现为例,包含答辩的问题和答案
java
主公不搬砖3 小时前
Nacos 2.5.2 国产信创 kingbase适配
java·docker·nacos·信创·kingbase·国产适配
cn_mengbei3 小时前
鸿蒙PC开发实战:Qt环境搭建保姆级教程与常见问题避坑指南(HarmonyOS 4.0+DevEco Studio 3.1最新版)
qt·华为·harmonyos
特立独行的猫a3 小时前
[鸿蒙PC命令行程序移植]:移植axel多线程高速下载工具踩坑记
华为·harmonyos·移植·鸿蒙pc·axel
谷隐凡二3 小时前
Kubernetes Route控制器简单介绍
java·容器·kubernetes
ssshooter3 小时前
复古话题:Vue2 的空格间距切换到 Vite 后消失了
前端·vue.js·面试
Haooog3 小时前
RAG医疗问答系统
java·大模型·项目·rag
luming-023 小时前
报错解决:IDEA终端输出和CMD终端输出Maven版本不一致
java·缓存·bug·intellij-idea
SmoothSailingT3 小时前
408每日一题——数据结构
数据结构·考研·408
MM_MS3 小时前
Halcon控制语句
java·大数据·前端·数据库·人工智能·算法·视觉检测