Description:
For an integer n, we call k>=2 a good base of n, if all digits of n base k are 1.
Now given a string representing n, you should return the smallest good base of n in string format.
Example 1:
Input: "13"
Output: "3"
Explanation: 13 base 3 is 111.
Example 2:
Input: "4681"
Output: "8"
Explanation: 4681 base 8 is 11111.
Example 3:
Input: "1000000000000000000"
Output: "999999999999999999"
Explanation: 1000000000000000000 base 999999999999999999 is 11.
Note:
1.The range of n is [3, 10^18].
2,The string representing n is always valid and will not have leading zeros.
问题描述:
对于n来说,如果一个大于等于2的进制k,能让n以k进制表示后每一位都是1,那么称k为一个好的进制
现在给定n,你需要返回最小的好的进制k
例子:
输入为”13”
输出为”3”
解释:13以3进制表示为111
问题分析:
令进制为k,我们知道n可以表示为:
n=k0+k1+k2+....km−1=km−1k−1n=k0+k1+k2+....km−1=km−1k−1
于是我们可以将n表示为形如f(k,m)f(k,m)的函数,我们知道k和m是成反比的关系,当f(k1,m1)=f(k2,m2),若m1>m2,那么必有k1<k2f(k1,m1)=f(k2,m2),若m1>m2,那么必有k1<k2,利用这个性质,我们可以由大到小遍历m,当m固定时,若能找到一个合适的k使得f(k,m)=nf(k,m)=n,那么k即是最小的解。现在虽然我们还不知道k的取值范围,不过在一个范围内找到一个满足条件的数,二分查找。
于是现在问题化作,如何找到m和k的范围.
- m的范围
由k和m成反比,当k等于2时,m最大,即2m−1=n2m−1=n,得m<=log(n+1)m<=log(n+1)
于是得到2<=m<=log(n+1)(注意m不能为1)2<=m<=log(n+1)(注意m不能为1) - k的范围
由n=k0+k1+k2+....km−1=km−1k−1n=k0+k1+k2+....km−1=km−1k−1,得
n<km−1n<km−1,得到k>(n+1)1mk>(n+1)1m
由n=k0+k1+k2+....km−1=km−1k−1n=k0+k1+k2+....km−1=km−1k−1,得
n>=km−1n>=km−1,得到k<=n1m−1k<=n1m−1
解法:
/*
如果想看更加完整和详细的解释,可以看看这篇链接:
https://leetcode.com/problems/smallest-good-base/discuss/96591/Java-O((logn)2)-binary-search-solution
*/
class Solution {
public String smallestGoodBase(String n) {
long num = Long.valueOf(n);
for(int m = (int)(Math.log(num + 1) / Math.log(2));m >= 2;m--){
//k的下界和上界
long l = (long)Math.pow(num + 1, 1.0 / m);
long r = (long)Math.pow(num, 1.0 / (m - 1));
//二分查找,一旦找到合适的k,k为最小解
while(l <= r){
long k = l + ((r - l) >> 1);
long f = 0L;
for(int i = 0;i < m;i++, f = f * k + 1);
if(num == f){
return String.valueOf(k);
}else if(num > f){
l = k + 1;
}else{
r = k - 1;
}
}
}
//注意,我们始终有k = num - 1,m = 2的解决方案
return String.valueOf(num - 1);
}
}