猜密码问题

一、题目描述

小杨申请了一个保密柜,但是他忘记了密码。只记得密码都是数字,而且所有数字都是不重复的。

请你根据他记住的数字范围和密码的最小数字数量,帮他算下有哪些可能的组合。

规则如下

  1. 输出的组合都是从可选的数字范围中选取的,且不能重复;
  2. 输出的密码数字要按照从小到大的顺序排列,密码组合需要按照字母顺序,从小到大的顺序排序。
  3. 输出的每一个组合的数字的数量要大于等于密码最小数字数量;
  4. 如果可能的组合为空,则返回"None"

二、输入输出描述

输入描述

  • 第一行:可能的密码数字列表,数字间以半角逗号分隔
  • 第二行:密码最小数字数量

输出描述

  • 可能的密码组合,每种组合显示成一行,每个组合内部的数字以半角逗号分隔,从小到大的顺序排列。
  • 输出的组合间需要按照字典序排序。比如:2,3,4放到2,4的前面

备注

字典序是指按照单词出现在字典的顺序进行排序的方法,比如:

  • a排在b前
  • a排在ab前
  • ab排在ac前
  • ac排在aca前

三、示例

|----|-----------------------------------------------|
| 输入 | 2,3,4 2 |
| 输出 | 2,3 2,3,4 2,4 3,4 |
| 说明 | 最小密码数量是两个,可能有三种组合: 2,3 2,4 3,4 三个密码有一种: 2,3,4 |

|----|---------------------------------|
| 输入 | 2,0 1 |
| 输出 | 0 0,2 2 |
| 说明 | 可能的密码组合,一个的有两种: 0 2 两个的有一个: 0,2 |

四、解题思路

  1. 核心思想

通过 **"数组升序排序 + DFS 递归回溯 + 树层去重"** 生成所有长度≥指定level的不重复升序组合,再对组合进行字典序排序后输出;核心是 "排序保证内部升序,递归回溯枚举所有组合,树层去重避免重复,字典序排序规范输出"。

  1. 问题本质分析
  • 表层问题:找出数组中所有长度≥level的不重复升序组合,按字典序排序输出;
  • 深层问题:
  1. 组合生成问题:本质是 "无重复元素的子集枚举"(子集长度≥level),需保证元素不重复选取且顺序升序;

  2. 去重问题:原数组存在重复元素时,需避免生成内容相同的组合,树层去重是高效解决方案;

  3. 顺序问题:需满足两个层级的顺序要求(组合内部数值升序、组合之间字典序);

  4. 边界问题:当数组长度 < level时,无合法组合,需返回None

  5. 核心逻辑

  • 排序预处理:对原数组升序排序,一是保证生成的组合内部天然升序,二是为树层去重提供条件(重复元素相邻);
  • 递归回溯枚举:以index为遍历起始点,递归选取元素构建路径,回溯重置路径,枚举所有长度≥level的组合;
  • 树层去重:跳过相邻重复元素,避免生成重复组合;
  • 结果处理:对合法组合进行字典序排序,按格式拼接输出,无结果则返回None
  1. 步骤拆解

  2. 输入解析与预处理

    • 读取逗号分隔的整数数组和最小长度level
    • 对整数数组进行升序排序,为组合内部升序和树层去重做准备。
  3. DFS 递归生成不重复组合

    • 初始化结果集合和路径容器(LinkedList,方便回溯移除末尾元素);
    • 调用 DFS 方法,传入起始索引0、空路径、结果集合:
    1. 递归终止条件(合法组合记录):若路径长度≥level,将路径转为逗号分隔的字符串,存入结果集合;
    2. 遍历数组:从当前index开始,避免重复选取元素;
    3. 树层去重:若当前元素与前一个元素重复,跳过该元素;
    4. 选择元素:将当前元素转为字符串加入路径;
    5. 递归深入:以i+1为新的起始索引,继续生成组合;
    6. 回溯重置:递归返回后,移除路径末尾的元素,准备选取下一个元素。
  4. 结果排序与输出

    • 若结果集合非空:对集合按字符串字典序排序,再用换行符拼接所有组合,返回拼接后的字符串;
    • 若结果集合为空:返回None

五、代码实现

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
  static int[] nums;
  static int level;

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

    nums = Arrays.stream(sc.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
    level = Integer.parseInt(sc.nextLine());

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

  public static String getResult() {
    // 按照数值大小升序,这样后续形成的组合的内部就是按照数值大小升序的
    Arrays.sort(nums);

    // 求不重复组合
    ArrayList<String> res = new ArrayList<>();
    dfs(0, new LinkedList<>(), res);

    if (res.size() > 0) {
      // 组合间按照字典序排序
      res.sort(String::compareTo);
      return String.join("\n", res);
    } else {
      return "None";
    }
  }

  public static void dfs(int index, LinkedList<String> path, ArrayList<String> res) {
    if (path.size() >= level) {
      // 如果path层数到达level层,则记录该组合
      res.add(String.join(",", path));
    }

    for (int i = index; i < nums.length; i++) {
      // 树层去重
      if (i > 0 && nums[i] == nums[i - 1]) continue;

      path.add(nums[i] + "");
      dfs(i + 1, path, res);
      path.removeLast();
    }
  }
}
相关推荐
fanruitian1 天前
visualstudio code cline使用mcp amap
java·前端·visual studio
ullio1 天前
div1+2. 2180C - XOR-factorization
算法
岁岁的O泡奶1 天前
NSSCTF_crypto_[LitCTF 2024]common_primes
开发语言·python·算法
骇客野人1 天前
基于springboot的Java快速定时任务
java·windows·spring boot
刘立军1 天前
程序员应该熟悉的概念(6)Fine-tuning和RAG
人工智能·算法·架构
踏浪无痕1 天前
RocketMQ 为什么读得这么快?揭秘 ConsumeQueue 的异步索引设计
后端·面试·rocketmq
资生算法程序员_畅想家_剑魔1 天前
Java常见技术分享-28-事务安全-事务日志-事务日志流程
java·开发语言
韭菜炒大葱1 天前
React 之 自定义 Hooks 🚀
前端·react.js·面试
韩师傅1 天前
从重启马车到常驻运输队:CGI 与 PHP 的物流系统演进简史
java·后端·php