【leetcode】483. 最小好进制 Java题解

以字符串的形式给出 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值