个人思路
- 好进制一定在
[2, n-1]
之间 - 若
n
的好进制是k
,那么应该存在一个i
使得pow(k, i)/(i-1) == n
,其中i
在[2, 60]
之间,也就是1的个数,为啥是60,因为最大值是1e18,也就是1000e6 < 1024*2^6 = 2^60 - 当我们确定了一个进制和一个1的个数,那么可以唯一确定一个整数,而观察两个变量的范围,显然选择1个个数作为外循环,选择进制作为内循环,且因为是找固定递增区间中的固定的一个数字,因此首先考虑到二分
- 总的时间复杂度是O(logn)
直接贴代码
class Solution {
public String smallestGoodBase(String n) {
long nLong = Long.parseLong(n);
long ans =nLong - 1;
for(int numOne = 60; numOne >= 2; --numOne){
long left = 2;
long right = nLong-1;
while( left <= right ){
long mid = left + (right - left >> 1);
long check = qinJiuZhao(nLong, mid, numOne);
if( check == nLong ){
return String.valueOf(mid);
}else if( check > nLong ){
right = mid - 1;
}else{
left = mid + 1;
}
}
}
return String.valueOf(ans);
}
public long qinJiuZhao(long nLong, long base, int numOne){
long num = 0;
for(int i = 0; i < numOne; ++i){
num = num * base + 1;
if( i < numOne - 1 && num > (nLong - 1) / base){
return nLong + 1;
}
}
return num;
}
}
需要注意的几个点:如何把一个字符串变成基础数据类型的整数,如何把一个基础数据类型的整数变成字符串;秦九昭算法中,如何防止溢出,千万不能把不等号右边的乘到左边,乘过去不就又超了吗,(用个锤子秦九昭,直接按幂相加保证不超)
方法二:数学推导
直接看题解吧,懒得写了,都是在考虑遍历1的数量2-60,倒序,接着在给定len的情况下找一个进制k
class Solution {
public String smallestGoodBase(String n) {
// (11...11)k = k^{s} + k^{s-1} + ... + k^1 + k^0 = n
// k^s < n < (k+1)^s
// k < n^{1/s} < k+1
long nLong = Long.parseLong(n);
long ans = nLong - 1;
int numOneMax = (int) Math.floor(Math.log(nLong) / Math.log(2));//1的最大个数,至少为2,此时进制最大为n-1,其实默认60也行
for (int m = numOneMax; m >= 2; m--) {
long base = (long) Math.pow(nLong, 1.0 / m);
long mul = 1, sum = 1;
for (int i = 0; i < m; i++) {//这里没用秦九昭算法
mul *= base;//为什么不会溢出可以思考一下
sum += mul;
}
if (sum == nLong) {
return Long.toString(base);
}
}
return Long.toString(nLong - 1);
}
}