483-smallest-good-base
Question
https://leetcode.com/problems/smallest-good-base/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:
- The range of n is [3, 10^18].
 - The string representing n is always valid and will not have leading zeros.
 
Thought Process
- Binary Search
- Define base to be k, and number of digits to be m
 - n = 1 + k^1 + k^2 + ... + k^(m-1)
 - Using geometric sequence summation, we know n = (1 - k^m)/(1 - k)
 - To find the bound for m and k, we need to use the above formula and other observation
 - There is one trivial solution when k = n - 1, then m = 2
 - When k = 2, which leads to n = 2^m - 1, then m = log(n + 1)
 - Since we at looking for the minimum k, we start with maximum m
 - Time complexity O((logn)^2)
 - Space complexity O(1)
 
 - Binary Search with tighter bound
- Let's redefine the m to be number of digits - 1 to make the number look pretty
 - n = 1 + k^1 + k^2 + ... + k^m = (k^(m+1) -1)/(k - 1)
 - We know that n > k^m, so n^(1/m) > k
 - We can also use binomial theorem to show that n < (k+1)^m, so n^(1/m) < k + 1
 - Basically, k < n^(1/m) < k + 1, so basically n^(1/m) is really close to our potential k
 - Moreover, rounding down the n^(1/m) will be the only candidate to be consider, because rounding up will make it too big (for example, k = 2, and n^(1/m) = 2.5)
 - Also for formula on ii, we can see that when k = 2, m is the max where m = log(n + 1)/log2 - 1 (again round down is preferred here, otherwise we will have number greater than n)
 - Another thing we can notice on formula ii is that the numerator is divisible by denominator, so we can just apply the formula on the right without worrying getting fraction
 - Time complexity O(logn)
 - Space complexity O(1)
 
 
Solution
import java.math.*;
class Solution {
    public String smallestGoodBase(String n) {
        long nL = Long.valueOf(n);
        int mHi = (int) (Math.log(nL + 1) / Math.log(2));
        long res = nL - 1;
        for (int m = mHi; m >= 2; m--) {
            long kLo = 2, kHi = nL - 1;
            while (kLo <= kHi) {
                long kMi = kLo + (kHi - kLo) / 2;
                BigInteger left = BigInteger.valueOf(nL).multiply(BigInteger.valueOf(kMi - 1)), right = BigInteger.valueOf(kMi).pow(m).subtract(BigInteger.valueOf(1));
                int cmp = left.compareTo(right);
                if (cmp == 0) return String.valueOf(kMi);
                else if (cmp < 0) kHi = kMi - 1;
                else kLo = kMi + 1;
            }
        }
        return String.valueOf(res);
    }
}
import java.math.*;
class Solution {
    public String smallestGoodBase(String n) {
        long nL = Long.valueOf(n);
        BigInteger nB = BigInteger.valueOf(nL);
        int mHi = (int) (Math.log(nL + 1) / Math.log(2)) - 1;
        long res = nL - 1;
        for (int m = mHi; m > 1; m--) {
            long k = (long) Math.pow(nL, 1.0 / m);
            BigInteger right = (BigInteger.valueOf(k).pow(m + 1).subtract(BigInteger.ONE)).divide(BigInteger.valueOf(k).subtract(BigInteger.ONE));
            // System.out.println("k = " + k + ", m = " + m + ", right = " + right);
            if (nB.compareTo(right) == 0) return String.valueOf(k);
        }
        return String.valueOf(res);
    }
}
class Solution {
    public String smallestGoodBase(String n) {
        long nL = Long.valueOf(n);
        int mHi = (int) (Math.log(nL + 1) / Math.log(2)) - 1;
        long res = nL - 1;
        for (int m = mHi; m > 1; m--) {
            long k = (long) Math.pow(nL, 1.0 / m);
            if (geometric(k, m) == nL) return String.valueOf(k); 
        }
        return String.valueOf(res);
    }
    private long geometric(long base, int m) {
        long res = 0;
        for (int i = 0; i <= m; i++) {
            res = 1 + res * base;
        }
        return res;
    }
}