[算法]谷歌笔试题:Beautiful Numbers
题目描述
思路
这道题就是在说,由多个1组成的数是beautiful的,现在想求出r进制中的r,使得给出的数字转换成r进制,得到beautiful的数字,如果有多种方式转换,那么取1的个数最多的那种情况,也就是r最小的情况。
这道题其实就是10进制和r进制的转化,这里就不详述了,但是注意题目中的一点就是给了两种数据集,小数据集的N最大为1000,大数据集的N最大为10^18,大数据集非常棘手。
先看看小数据集的常规思路,就从二进制开始进行循环,每次循环判断一下该进制下进行转换得到的数字是否为beautiful的,见下面第一个代码。这种思路的时间复杂度是NlogN,因为N最大为1000,所以很快。
再看看大数据集,如果我们采用上面那种方式的话,N为10^18,这样带进去差不多是64*10^18的计算,假设计算机1秒执行10^8次计算,这样的耗时也是非常巨大的。
因此对于大数据集的思路是,我们先看一下,10^18的数字,转成二进制,最多的话,也是64个1,所以我们就设开始是64个1,然后递减,计算每次该个数的1能不能由某一进制转换成功。在计算进制的过程中,可以采用二分查找,这样总共的时间复杂度就是logN*logN*logN,相当于64*64*64,还是非常小的。最后因为可能数据溢出,所以用long,用long也可能在计算过程中溢出,所以做了一下处理,这里同样可以采用BigInteger。
代码
小数据集的代码(常规思路):
package interview.google; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Scanner; public class BeautifulNumber { public static void main(String[] args) { Scanner in = new Scanner( new BufferedReader(new InputStreamReader(System.in))); int cases = in.nextInt(); for (int i = 1; i <= cases; ++i) { long n = in.nextLong(); System.out.println("Case #" + i + ": " + beautiful(n)); } } private static long beautiful(long n) { for (long radix = 2; radix < n; radix++) { if (isBeautiful(n, radix)) { return radix; } } throw new IllegalStateException("Should not reach here."); } private static boolean isBeautiful(long n, long radix) { while (n > 0) { if (n % radix != 1) { return false; } n /= radix; } return true; } }
大数据集的代码(同样适用于小数据集):
package interview.google; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Scanner; public class BeautifulNumberLarge { public static void main(String[] args) { Scanner in = new Scanner( new BufferedReader(new InputStreamReader(System.in))); int cases = in.nextInt(); for (int i = 1; i <= cases; ++i) { long n = in.nextLong(); System.out.println("Case #" + i + ": " + beautiful(n)); } } private static long beautiful(long n) { for (int bits = 64; bits >= 2; bits--) { long radix = getRadix(n, bits); if (radix != -1) { return radix; } } throw new IllegalStateException("Should not reach here."); } /** * Gets radix so that n is 111...1 (bits 1 in total) in that * radix.二分查找,最大值需要+1. * * @return the radix. -1 if there's no such radix. */ private static long getRadix(long n, int bits) { long minRadix = 2; long maxRadix = n; while (minRadix < maxRadix) { long m = minRadix + (maxRadix - minRadix) / 2; long t = convert(m, bits); if (t == n) { return m; } else if (t < n) { minRadix = m + 1; } else { maxRadix = m; } } return -1; } /** * Returns the value of 111...1 (bits 1 in total) in radix. */ private static long convert(long radix, int bits) { long component = 1; long sum = 0; for (int i = 0; i < bits; i++) { if (Long.MAX_VALUE - sum < component) { sum = Long.MAX_VALUE; } else { sum += component; } if (Long.MAX_VALUE / component < radix) { component = Long.MAX_VALUE; } else { component *= radix; } } return sum; } }