java幸运数 4 7_幸运数字问题 —— Java实现

题目描述

小雅同学认为6,8是她的幸运数字,而其他数字均不是,一个幸运数是指在十进制表示下只含有幸运数字的数。给定你一个区间(a,b)a和b之间(其中包括a和b幸)运数的个数。

输入描述

输入两个整数a和b,a的取值范围在1和1000000000之间(其中包括1和1000000000),b的取值范围在a和1000000000之间(其中包括a和1000000000)。

输出描述

返回a和b之间的幸运数个数,如果入参不合法,请输出-1

时间、空间要求

时间限制:1秒 空间限制:32768K

示例

输入

1 10

输出

2

说明

6,8,6666,88888,6668888,68686688均为幸运数字,当a=1,b=10函数返回值为2。

解题思路

首先一看数量级,如果用遍历,然后挨个判断显然是不行的,肯定会超出时间限制,通过数学枚举找规律发现:这就是一个全排列问题,100 - 1000 的幸运数的在1- 10的所有幸运数的基础上跟‘6’和‘8’全排一下组成百位级别的幸运数,以此类推。换句话说,每个区间内的幸运数是已知的(总共2^ i个,i为数的位数,比如个位数(i == 1),就是从‘6’、 ‘8’中选i个数全排列组成一个整数),怎样得到每个区间的幸运数n?就是通过上一个区间迭代得到。这样将一个时间复杂度为O(N)(而且要去判断该数是否是幸运数,这个复杂度系数应该会很大!!!)变为了复杂度O(logN).

代码

package string;

/**

* 幸运数字

* 最佳时间43ms,时间复杂度O(logN)

* 特别注意的是,某几个+=操作,通过寄存器简单的加减指令,可以优化20ms左右,这也是让我更加注重让自己的代码尽量清爽简洁,以便于提升!

*/

import java.util.Scanner;

public class ImprovedLuckyNum {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int a = sc.nextInt();

int b = sc.nextInt();

int INF = 1000000000;

int[] before = new int[512];

int[] after = new int[512];

if (a < 1 || a > INF || a > b) {

System.out.println(-1);

} else {

int sum = 0;

for (int i = 1; i <= 9; i++) {

int length = (int)Math.pow(2, i); // 这个区段有多少个这样的数

// 判断超过范围即可退出了

if (Math.pow(10, i) > b) {

break;

}

if (i == 1) {

after[0] = 6;

after[1] = 8;

} else {

int b_l = (int)Math.pow(2, i - 1); // 表示before数组的包含有效值的长度

for (int k = 0; k < b_l; k++) {

after[k] = before[k] * 10 + 6;

after[k + b_l] = before[k] * 10 + 8;

}

}

// 寻找合适的数

for (int j = 0; j < length; j++) {

if (after[j] >= a && b >= after[j]) {

sum++;

}

// 记得更新before数组,用于迭代

before[j] = after[j];

// after[j] = INF + 1;

}

}

System.out.println(sum);

}

}

}

运行结果

时间在50ms左右,空间使用在10000k左右,基本上快赶上C/C++的时间了(C/ C++时间大概在30ms左右)

另外:减少一些寄存器指令也可以起到一定的优化效果,10ms左右

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值