把1,2,3,4,5,6,7,8,9九个数分成三组,各个数字使用一次

前几天找工作,笔试下面这道题:
把1,2,3,4,5,6,7,8,9共九个数分成三组构成排列a1a2a3,a4a5a6,a7a8a9,而且每个数字使用有且仅有一次,构成的排列之比为3:2:1,求输出所有的排列组合。

方法一:
显然a1a2a3,a4a5a6,a7a8a9在[123, 987]内,进一步就是a1a2a3在[123,329],遍历即可

public static void test1(){
	long e1 = System.currentTimeMillis();
	for(int i = 123; i <= 329; i ++){
		if(match(i, 2*i, 3*i)){
			System.out.println(i + "	" + 2*i + "	" + 3*i);
		}
	}
	long e2 = System.currentTimeMillis();
	System.out.println("time:" + (e2 - e1));
}

public static boolean match(int first, int second, int third) {
	List<Integer> list = Arrays.asList(0, 0, 0, 0, 0, 0, 0, 0, 0);
	int index = -1;
	String str = first +  "" + second +  "" + third;
	for(int i = 0; i < str.length(); i ++){//遍历每一个字符,在list中记录对应字符出现的次数
		index = Integer.parseInt(str.substring(i, i + 1)) - 1;//index为零则是取到字符0,需要过滤掉
		if(index < 0 || index > 8){//过滤
			return false;
		}
		if(list.get(index) >= 1){
			return false;
		}else{
			list.set(index, list.get(index) + 1);
		}
	}
	return true;
}
结果:
192	384	576
219	438	657
273	546	819
327	654	981
time:1

方法二:
使用正则表达式过滤

public static void test1() {
	long e1 = System.currentTimeMillis();
	Pattern pattern = Pattern.compile(//不重复字符组成的九位数
				  "([1-9])"
				+ "(?!\\1)([1-9])"
				+ "(?!\\1|\\2)([1-9])"
				+ "(?!\\1|\\2|\\3)([1-9])"
				+ "(?!\\1|\\2|\\3|\\4)([1-9])"
				+ "(?!\\1|\\2|\\3|\\4|\\5)([1-9])"
				+ "(?!\\1|\\2|\\3|\\4|\\5|\\6)([1-9])"
				+ "(?!\\1|\\2|\\3|\\4|\\5|\\6|\\7)([1-9])"
				+ "(?!\\1|\\2|\\3|\\4|\\5|\\6|\\7|\\8)([1-9])$");
	String str = "";
	Matcher match = null;
	for (int i = 123; i <= 329; i++) {
		if (3 * i >= 1000) {
			break;
		}
		str = Integer.toString(i * 1000000 + i * 2 * 1000 + 3 * i);
		match = pattern.matcher(str);
		if (match.matches())
			System.out.println(i + " " + 2 * i + " " + 3 * i);
	}
	long e2 = System.currentTimeMillis();
	System.out.println("time:" + (e2 - e1));
}
结果:
192	384	576
219	438	657
273	546	819
327	654	981
time:3
### 回答1: 这是一个数位统计的问题。我们可以将区间内的每个数拆分成个位、十位、百位等等,然后统计每个数位上数字x出现的次数,最后将所有数位上的次数相加即可。 以数字1为例,对于个位上的数字1,我们可以发现,每10个数中就有1个数的个位是1。例如,在1到20中,有2个数的个位是1,即1和11。在21到30中,也有2个数的个位是1,即21和31。因此,在1到n中,个位上数字1出现的次数为n/10*1+(n%10>=1&&n%10<1)*1。 对于十位、百位等等,我们可以采用类似的方法进行统计。最终,所有数位上数字x出现的次数为各个数位上数字x出现的次数之和。 具体实现可以使用循环遍历区间内的每个数,然后将每个数拆分成个数位上的数字,统计数字x在每个数位上出现的次数,最后将所有次数相加即可。 ### 回答2: 本题可以通过计算每个数位上出现x的次数来掌握该数字在区间1到n中出现的次数。设当前计算的位数为第i位,低位为a,高位为b,则有以下三种情况: 1. a < x,即第i位小于x,那么第i位上出现x的次数只受高位b的影响。比如,在计算数字1在1到11中出现的次数时,第一位是1,低位为0,高位为1,此时第一位上出现1的次数只受高位1的影响,即在10到19之间,第一位上出现1的次数为10。 2. a = x,即第i位等于x,那么第i位上出现x的次数不仅受高位b的影响,还要受低位a的影响。比如,在计算数字1在1到11中出现的次数时,第二位也是1,此时第二位上出现1的次数受高位1和低位0的影响,即在1到9之间,第二位上出现1的次数为2。 3. a > x,即第i位大于x,那么第i位上出现x的次数只受高位b的影响。比如,在计算数字1在1到11中出现的次数时,第二位是0,低位为1,高位为1,此时第二位上出现1的次数只受高位1的影响,即在10到19之间,第二位上出现1的次数为10。 综上所述,我们可以得到以下公式: total = 0 for i in range(len(str(n))): a = int(str(n)[i]) b = int(str(n)[:i] or 0) c = int(str(n)[i+1:] or 0) total += b * 10**(len(str(n))-i-1) if a > x: total += 10**(len(str(n))-i-1) elif a == x: total += c + 1 其中,b表示高位,c表示低位,total表示数字x在1到n中出现的次数。 最后,我们可以调用以上代码来计算数字x在1到n中出现的次数。 ### 回答3: 这道题目是一道数位分析的题目,经常被应用在计数问题的解决过程中。我们首先需要分析题干中的数据,题干中给出的是一个区间[1, n],同时需要计算数字x在该区间中出现的次数。 首先我们需要分析数字x所在的位置,数字x在每一个数位上均有可能出现,分别是个位上,十位上,百位上,千位上以及更高位上。因此,我们可以通过逐个数位去分析数字x的出现次数,然后将所有可能出现的位置的次数加起来即为数字x在区间[1, n]中出现的总次数。 我们以数字x为1为例,来分析数字1在每一个数位上出现的次数。首先是个位上,当个位上的数是1时,个位上数字1会出现1次,当个位上的数比1小时,个位上数字1均不会出现,因此个位上数字1的出现次数为n/10 + (n mod 10 >= 1 ? 1 : 0)。 接着分析十位上数字1的出现次数,当十位上的数是1时,十位上数字1会在数值为10~19,110~119, 210~219, …… (n/100) * 10 + (n mod 100 >= 10 ? 10 : 0) + (n mod 100 - 10 + 1 > 0 ? n mod 100 - 10 + 1 : 0) 同理,因为数字1在每一个数位上均有可能出现,可以逐个数位分析,计算出数字1在每个数位上出现的次数,然后将各个数位上数字1的出现次数加起来即为数字1在区间[1, n]中出现的总次数。 类似地,计算其他数字在区间[1, n]中出现的总次数同样可以采用这种方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值