题目描述
对于给定的整数 n
, 如果 n
的 k (k>=2)
进制数的所有数位全为 1
,则称 k (k>=2)
是 n
的一个 好进制。
以字符串的形式给出 n
, 以字符串的形式返回 n
的最小好进制。
样例
输入:"13"
输出:"3"
解释:13 的 3 进制是 111。
输入:"4681"
输出:"8"
解释:4681 的 8 进制是 11111。
输入:"1000000000000000000"
输出:"999999999999999999"
解释:1000000000000000000 的 999999999999999999 进制是 11。
注意
- n 的取值范围是 [3, 10^18]。
- 输入总是有效且没有前导 0。
算法
(枚举) $O(\log^2 n)$
- 假设进制为 $k$,所有数位都是 1 且共有 $t$ 位时,表示的数在十进制下为 $k^0 + k^1 + k^2 + … + k^{t-1}$。
- 显然 $t$ 越大,可能的 $k$ 就会越小。由于 $n$ 最大为 $10^{18}$,所以我们可以从 $1 + \lceil \log n \rceil$ 开始向下枚举 $t$。最坏情况下,$t=2$ 时必定存在 $k=n-1$ 满足条件,故枚举的下界为 3。
- 固定 $t$ 后,计算一个待验证的 $k = \lfloor \sqrt[t-1]{n} \rfloor$。
- 接下来,验证这一对 $k$ 和 $t$ 是否合法。
时间复杂度
- 枚举数位的时间为 $O(\log n)$,
pow
函数计算的复杂度为 $O(\log n)$。 - 故总时间复杂度为 $O(\log^2 n)$。
C++ 代码
#define LL long long
class Solution {
public:
string smallestGoodBase(string n) {
LL N = stoll(n);
for (int t = log2(N) + 1; t >= 3; t--) {
LL k = pow(N, 1.0 / (t - 1));
LL tot = 0;
for (int i = 0; i < t; i++) {
tot = tot * k + 1;
if (tot > N)
break;
}
if (tot == N)
return to_string(k);
}
return to_string(N - 1);
}
};
tot在计算过程中有可能越界么
加了一个判断,防止越界。当然这个判断不是完全可靠的,但越界了除了手写高精度也没有任何办法
嗯嗯
pow好像是O(1)的?
在特定的精度内可以看做常数,但无损精度的情况下就是 $\log n$ 的,这里为了通用性,就用了 $\log n$。
类似实现
想问一下为啥我看到有些人的代码风格和你这种很像,你们是学什么竞赛要求这种风格吗,好像很大多数人风格不太一样
这个是 functional programming