You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.1 KiB

2 years ago
package class24;
// 里程表不能含有4给定一个数num返回他是里程表里的第几个
public class Code03_NotContains4 {
// num中一定没有4这个数字
public static long notContains4Nums1(long num) {
long count = 0;
for (long i = 1; i <= num; i++) {
if (isNot4(i)) {
count++;
}
}
return count;
}
public static boolean isNot4(long num) {
while (num != 0) {
if (num % 10 == 4) {
return false;
}
num /= 10;
}
return true;
}
// arr[1] : 比1位数还少1位有几个数(数字里可以包含0但是不可以包含4)0个
// arr[2] : 比2位数还少1位有几个数(数字里可以包含0但是不可以包含4)9个
// 1 -> 0 1 2 3 - 5 6 7 8 9 = 9
// arr[3] :比3位数还少1位有几个数(数字里可以包含0但是不可以包含4)81个
// 1 : 0 (0 1 2 3 - 5 6 7 8 9) = 9
// 2 :
// 1 (0 1 2 3 - 5 6 7 8 9) = 9
// 2 (0 1 2 3 - 5 6 7 8 9) = 9
// 3 (0 1 2 3 - 5 6 7 8 9) = 9
// 5 (0 1 2 3 - 5 6 7 8 9) = 9
// ...
// 9 (0 1 2 3 - 5 6 7 8 9) = 9
public static long[] arr = { 0L, 1L, 9L, 81L, 729L, 6561L, 59049L, 531441L, 4782969L, 43046721L, 387420489L,
3486784401L, 31381059609L, 282429536481L, 2541865828329L, 22876792454961L, 205891132094649L,
1853020188851841L, 16677181699666569L, 150094635296999121L, 1350851717672992089L };
// num中一定没有4这个数字
public static long notContains4Nums2(long num) {
if (num <= 0) {
return 0;
}
// num的十进制位数len
int len = decimalLength(num);
// 35621
// 10000
long offset = offset(len);
// 第一位数字
long first = num / offset;
return arr[len] - 1 + (first - (first < 4 ? 1 : 2)) * arr[len] + process(num % offset, offset / 10, len - 1);
}
// num之前有开头
// 在算0的情况下num是第几个数字
// num 76217
// 10000
// 6217
// 1000
// len
public static long process(long num, long offset, int len) {
if (len == 0) {
return 1;
}
long first = num / offset;
return (first < 4 ? first : (first - 1)) * arr[len] + process(num % offset, offset / 10, len - 1);
}
// num的十进制位数
// num = 7653210
// 7
public static int decimalLength(long num) {
int len = 0;
while (num != 0) {
len++;
num /= 10;
}
return len;
}
// len = 6
// 100000
// len = 4
// 1000
// 3452168
// 1000000
// 3
public static long offset(int len) {
long offset = 1;
for (int i = 1; i < len; i++) {
offset *= 10L;
}
return offset;
}
// 讲完之后想到了课上同学的留言
// 突然意识到这道题的本质是一个9进制的数转成10进制的数
// 不过好在课上的解法有实际意义,就是这种求解的方式,很多题目都这么弄
// 还有课上的时间复杂度和"9进制的数转成10进制的数"的做法时间复杂度都是O(lg N)
// 不过"9进制的数转成10进制的数"毫无疑问是最优解
public static long notContains4Nums3(long num) {
if (num <= 0) {
return 0;
}
long ans = 0;
for (long base = 1, cur = 0; num != 0; num /= 10, base *= 9) {
cur = num % 10;
ans += (cur < 4 ? cur : cur - 1) * base;
}
return ans;
}
public static void main(String[] args) {
long max = 88888888L;
System.out.println("功能测试开始,验证 0 ~ " + max + " 以内所有的结果");
for (long i = 0; i <= max; i++) {
// 测试的时候输入的数字i里不能含有4这是题目的规定
if (isNot4(i) && notContains4Nums2(i) != notContains4Nums3(i)) {
System.out.println("Oops!");
}
}
System.out.println("如果没有打印Oops说明验证通过");
long num = 8173528638135L;
long start;
long end;
System.out.println("性能测试开始,计算 num = " + num + " 的答案");
start = System.currentTimeMillis();
long ans2 = notContains4Nums2(num);
end = System.currentTimeMillis();
System.out.println("方法二答案 : " + ans2 + ", 运行时间 : " + (end - start) + " ms");
start = System.currentTimeMillis();
long ans3 = notContains4Nums3(num);
end = System.currentTimeMillis();
System.out.println("方法三答案 : " + ans3 + ", 运行时间 : " + (end - start) + " ms");
}
}