概述
以字符串的形式给出 n , 以字符串的形式返回 n 的最小好进制 。
如果 n 的 k(k>=2) 进制数的所有数位全为1,则称 k(k>=2) 是 n 的一个好进制 。
示例 1:
输入:n = "13"
输出:"3"
解释:13 的 3 进制是 111。
示例 2:
输入:n = "4681"
输出:"8"
解释:4681 的 8 进制是 11111。
示例 3:
输入:n = "1000000000000000000"
输出:"999999999999999999"
解释:1000000000000000000 的 999999999999999999 进制是 11。
提示:
- n 的取值范围是 [3, 10^18]
- n 没有前导 0
解析:
这是一道数学题目。
首先:
(n)10(10进制) = (1111…111)(k)(k进制)
假设k进制数共有len个1:
那么:n = 1 * k^0 + 1 * k^1 + … + 1 * k^(len - 1)
由于题目让求的是最小好进制是多少。
可以得到,如果k(进制)越大,len就越少。k越小,len就越长。
如果:len = 1 有:n = 1, 题目要求n >=3
如果:len = 2 有:n = k^0 + k^1, k = n - 1,这就是len可以到达的最大值。题意说:k >= 2
即:2<= k <= n - 1
那么进制就有了范围了。
然后:
那么对于len的范围有吗?如果len的范围也有,把就可以枚举len,枚举k进而求出答案了。
从上面的分析得到:当k = 2时,len最长。要是把这个最长给计算出来,就可以得到len的范围了。
k = 2,也就是二进制,2^len - 1 = n, len = log2(n + 1),
所以len最长就是log2(n + 1)。显然这个数字不是很大。即使是n = 1^18,也没有多少。最小是:2,因为n >=3, 3对应的二进制数字就是11,所以len最小取2
那么就可以进行枚举了。
k表示进制,len表示1的长度。
2 <= k <= n - 1, 2 <= len <= log2(n + 1) 时间复杂度可以达到:O(klen) = O(nlogn)
优化:
由于题目让求的是最小好进制数,那么就要从len最长开始递减遍历,因为越长,进制越小。
进制是单调的,对于一个len,n = k^0 + k^1 +…+ k^(len - 1),故n的大小和进制的大小成正比。遍历其所有可行进制。完全可以采用二分的方式查找。如果当前进制对应值大于等于了n,那就向进制小的进制方向找。否则向大的方向找。时间复杂度:O(logn * logn)
代码中:len >= 3而不是 len >= 2的原因是:k <= n - 1
class Solution {
public String smallestGoodBase(String n) {
long m = Long.parseLong(n);
int max = (int) ((Math.log(m) / Math.log(2))) + 1;
for (int len = max; len >= 2; len--) {
// 进制 最大为m - 1
long l = 2, r = m - 1;
while (l < r) {
// 进制
long mid = (l + r) >> 1;
// 找最小好进制 >= r = mid
if (check(mid, len, m) >= 0) {
r = mid;
} else {
l = mid + 1;
}
}
if (check(r, len, m) == 0) {
return String.valueOf(r);
}
}
return String.valueOf(m - 1);
}
private static int check(long k, int len, long m) {
long ans = 1;
// 计算k^0 + k^1 + k^2 + ... + k^(len - 1)
for (int i = 1; i < len; i++) {
if (ans > (m - 1) / k) return 1; // 如果ans > m了,返回1,在左半边找。r = mid
ans = ans * k + 1;
}
if (ans == m) return 0;
return ans - m > 0 ? 1 : -1;
}
}
再优化:
观察n = k^0 + k^1 + … + k^(len - 1),令len - 1 = s;
由二项式定理可得:
(k + 1)^s = Cs0 * k^0 + Cs1*k^1 + … + CssK^s > n
而n > k^s, 故:k^s < n < (k + 1)^s 故:k < n^1/s < k + 1
所以:取n^1/s为整(直接int转换向下取整)就是我们要找的k进制。
时间复杂度:O(logn
class Solution {
public String smallestGoodBase(String n) {
long m = Long.parseLong(n);
int max = (int) (Math.log(m + 1) / Math.log(2));
for (int len = max; len >= 2; len--) {
// 长度越大 进制数越小
long k =(long) (Math.pow(m, 1.0 / (len - 1)));
long res = 1;
for (int i = 1; i < len; i++) res = res * k + 1;
if (res == m) return String.valueOf(k);
}
return String.valueOf(m - 1);
}
}
最后
以上就是外向硬币为你收集整理的【leetcode】483. 最小好进制 Java题解的全部内容,希望文章能够帮你解决【leetcode】483. 最小好进制 Java题解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复