最大数字问题

一、题目描述

给定一个由纯数字组成以字符串表示的数值,现要求字符串中的每个数字最多只能出现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();
  }
}
相关推荐
GIS瞧葩菜2 分钟前
Cesium 轴拖拽 + 旋转圈拖拽 核心数学知识
人工智能·算法·机器学习
m0_686041618 分钟前
C++中的适配器模式变体
开发语言·c++·算法
txzrxz9 分钟前
结构体排序,双指针,单调栈
数据结构·算法·双指针算法·单调栈·结构体排序
AndrewHZ12 分钟前
【AI黑话日日新】什么是AI智能体?
人工智能·算法·语言模型·大模型·llm·ai智能体
wWYy.14 分钟前
算法:二叉树最大路径和
数据结构·算法
葱明撅腚15 分钟前
利用Python挖掘城市数据
python·算法·gis·聚类
We་ct18 分钟前
LeetCode 36. 有效的数独:Set实现哈希表最优解
前端·算法·leetcode·typescript·散列表
weixin_3954489129 分钟前
main.c_cursor_0129
前端·网络·算法
RANCE_atttackkk31 分钟前
Springboot+langchain4j的RAG检索增强生成
java·开发语言·spring boot·后端·spring·ai·ai编程
hello 早上好41 分钟前
03_JVM(Java Virtual Machine)的生命周期
java·开发语言·jvm