最大数字问题

一、题目描述

给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现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();
  }
}
相关推荐
云原生指北18 分钟前
GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent
java
仰泳的熊猫1 小时前
题目2570:蓝桥杯2020年第十一届省赛真题-成绩分析
数据结构·c++·算法·蓝桥杯
似水明俊德4 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net
Leinwin4 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
无极低码5 小时前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
薛定谔的悦5 小时前
MQTT通信协议业务层实现的完整开发流程
java·后端·mqtt·struts
软件算法开发5 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
enjoy嚣士5 小时前
springboot之Exel工具类
java·spring boot·后端·easyexcel·excel工具类
罗超驿5 小时前
独立实现双向链表_LinkedList
java·数据结构·链表·linkedlist