21天学习挑战赛--猜密码

本文介绍了一种基于全排列算法的密码组合生成方法,用于解决在指定数字范围内寻找符合特定条件的所有可能密码组合的问题。该算法首先对输入数字进行升序排序,然后递归地生成不同长度的数字组合。

活动地址:CSDN21天学习挑战赛

题目描述

小杨申请了一个保险柜,但是忘记了密码。只记得密码都是数字,而且数字都是不重复的。请根据数字范围和密码的最小数字数量,计算所有可能的密码组合,规则如下:

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

输入描述

输入的第一行是可能的密码数字列表,数字间以半角逗号分隔;

输入的第二行是密码最小数字数量。

输出描述

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

示例

输入

2,3,4

2

输出

2,3

2,3,4

2,4

3,4

思考

原博主 给出的解题思路如下:

1、因为密码是升序的,所以首先要对输入的数字进行升序排序。

2、根据题意是从可选的密码数字列表中选出对应的数量的数字作为密码。也就是从M个字符中取出N个字符的全排列(经典算法)

3、因为最小数字数量是N,所以需要一直遍历到可选密码数字列表的长度,才能得到所以可能的密码组合。

我的思考

题目的意思是从给定的M个数字中取出长度在[N,M]区间内的所有数字组合,并将得到的数字组合按照字典顺序排序。

中间较复杂的问题就是取长度在[N,M]变化的数字组合,简化一下就是取定长L的数字组合,这样就简单多了。

代码实现

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String[] strings = sc.nextLine().split(",");
        int n = sc.nextInt();
        
        //对输入数字进行升序排序
        Arrays.sort(strings);
        //存放数字组合结果
        List<String> list = new ArrayList<>();
        //数组长度l在区间[N,M]间移动
        for(int l=n;i<=strings.length;l++) {
            combine(list,strings,i,new String(),0);
        }
        //将数字组合排序
        Collections.sort(list);
        if(list.size() == 0) {
            System.out.println("None");
        }else {
            list.stream().
                forEach(System.out::println);
        }
    }
  
    public static void combine(List<String> list, String[] str, int n, String res, int index) {
        //list  存储数字组合的结果
        //str  排序后的输入数字组合
        //n  数字组合的长度
        //res  数字组合的字符串
        //index  开始循环的下标
        if(n == 0) {
            //一个数字组合拼接完毕,放入存储
            list.add(res);
        }else{
            //从index处开始循环,一个个拼接
            for(int i=index;i<str.length;i++) {
                //拼接
                if(index == 0)
                	res += str[i];
                else
                    res += "," + str[i];
                //进入下一个分支
                combine(list,str,n-1,res,i+1);
                //将刚刚拼接的最后一个数字剔除
                if(index == 0) {
                	res = res.substring(0, res.length()-1);
                }else {
                    res = res.substring(0,res.length() - - 2);
                }
            }
        }
    }
}

我的理解和收获

本题目中最复杂的部分就是从长度为M的数字中取长度为L的数字组合,这其实很像分治。

比如从1,2,3,4中取两个数字:

先取1,在【2,3,4】中取剩下的一个,有三种;

然后取2,在【3,4】中取剩下的一个,有两种;

然后取3,在【4】中取剩下的一个,有一种;

或者1,2,3,4中取三个数字:

先取1,在【2,3,4】中取剩下的两个:

先取2,在【3,4】中取剩下的一个,有两种;

然后取3,在【4】中取剩下的一个,有一种。

所以这里有三种情况;

然后取2,在【3,4】中取剩下的两个:

先取3,然后在【4】中取剩下的一个,有一种;

所以这里有一种情况。

加一起有四种情况

推广一下就是

从M个字符中取L个字符:

先取一个,在剩下的M-1个字符中取L-1个字符:

先取一个,在剩下的M-2个字符中取L-2个字符:

总结一下,就是切割字符,然后从剩下的字符中取字符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值