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.
The range of n is [3, 10^18].
Example 1:
Input: “13”
Output: “3”
Explanation: 13 base 3 is 111.
这道题刚开始看的时候毫无头绪,感觉唯一合适的思路就是穷举了,从base为2开始一直开始往前试(因为是找最小的基,因此要从小到大的开始试),试到的那个数如果满足条件那么我们就找到了答案。这样的方法显然很笨,绝对的超时。后来看了答案,才恍然大悟,有时候换个角度去思考问题,往往能得到不错的结果。
题目中已经说了,要找到目标数aim的全‘1’表示的base。那么也就是说无论如何最后该aim的表示形式一定是全‘1’的,如果是最小的base那么也就意味着最长的‘1’串。我们可以固定一个‘1’串来找是否有合适的base可以满足,而在固定‘1’串找base的时候可以使用二分查找来节省时间。如果当前的‘1’串都不合适,那么我们就可以减少‘1’串的长度,知道找到为止,这时候‘1’串对应的base一定是最小的base。由于aim的范围是在[3, 10^18],那么‘1’串最长无非是base为2的时候(这里的放缩其实可以适当的放大范围,并不影响整体的复杂度)。
class Solution {
public:
string smallestGoodBase(string n) {
unsigned long long tn=(unsigned long long)stoll(n);
unsigned long long x=1;
for (int i=62;i>=2;i--) {
if ((x<<i)<tn) {
unsigned long long cur=mysolve(tn,i);
if (cur!=0) return to_string(cur);
}
}
return to_string(tn-1);
}
unsigned long long mysolve(unsigned long long n,int d) {
double tn=(double) n;
unsigned long long right=(unsigned long long)(pow(tn,1.0/d)+1);
unsigned long long left=2;
while (left<=right){
unsigned long long mid=left+(right-left)/2;
unsigned long long sum=1,cur=1;
for (int i=1;i<=d;i++) {
cur*=mid;
sum+=cur;
}
if (sum==n) return mid;
if (sum>n) right=mid-1;
else left=mid+1;
}
return 0;
}
};