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.
這道題讓我們求最小的好基數,定義了一個大於等於2的基數k,如果可以把數字n轉化為各位都是1的數,那么就稱這個基數k是好基數。通過看題目中的三個例子,應該大致可以理解題意了吧。如果我們用k表示基數,m表示轉為全1數字的位數,那么數字n就可以拆分為:
n = 1 + k + k^2 + k^3 + ... + k^(m-1)
這是一個等比數列,中學數學的內容吧,利用求和公式可以表示為 n = (k^m - 1) / (k - 1)。我們的目標是求最小的k,那么仔細觀察這個式子,在n恆定的情況,k越小則m卻大,就是說上面的等式越長越好。下面我們來分析m的取值范圍,題目中給了n的范圍,是 [3, 10^18]。由於k至少為2,n至少為3,那么肯定至少有兩項,則 m>=2。再來看m的上限該如何求?其實也不難,想要m最大,k就要最小,k最小是2,那么m最大只能為 log2(n + 1),數字n用二進制表示的時候可拆分出的項最多。但這道題要求變換后的數各位都是1,那么我們看題目中最后一個例子,可以發現,當 k=n-1 時,一定能變成 11,所以實在找不到更小的情況下就返回 n-1。
下面我們來確定k的范圍,由於k至少為2,那么就可以根據下面這個不等式來求k的上限:
n = 1 + k + k^2 + k^3 + ... + k^(m-1) > k^(m-1)
解出 k < n^(1 / (m-1)),其實我們也可以可以通過 n < k^m - 1 來求出k的准確的下限,但由於是二分查找法,下限直接使用2也沒啥問題。分析到這里,那么解法應該已經躍然紙上了,我們遍歷所有可能的m值,然后利用二分查找法來確定k的值,對每一個k值,我們通過聯合m值算出總和 sum,然后跟n來對比即可,參見代碼如下:
class Solution { public: string smallestGoodBase(string n) { long long num = stol(n); for (int i = log(num + 1) / log(2); i >= 2; --i) { long long left = 2, right = pow(num, 1.0 / (i - 1)) + 1; while (left < right) { long long mid = left + (right - left) / 2, sum = 0; for (int j = 0; j < i; ++j) { sum = sum * mid + 1; } if (sum == num) return to_string(mid); if (sum < num) left = mid + 1; else right = mid; } } return to_string(num - 1); } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/483
參考資料: