1. 问题描述:
对于给定的整数 n, 如果n的k(k>=2)进制数的所有数位全为1,则称 k(k>=2)是 n 的一个好进制。以字符串的形式给出 n, 以字符串的形式返回 n 的最小好进制。
示例 1:
输入:"13"
输出:"3"
解释:13 的 3 进制是 111。
示例 2:
输入:"4681"
输出:"8"
解释:4681 的 8 进制是 11111。
示例 3:
输入:"1000000000000000000"
输出:"999999999999999999"
解释:1000000000000000000 的 999999999999999999 进制是 11。
提示:
n的取值范围是 [3, 10^18]。
输入总是有效且没有前导 0。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/smallest-good-base
2. 思路分析:
① 对于这种题目我们可以从代数的角度来表示成一个对应的式子,根据题目的描述,我们不妨设n由k进制下的0,1,2...m位1构成的,这样就可以n就可以表示成k和m的方程,表示成方程之后我们的目标是找到m,k使得这个方程成立,我们可以大概估计一下m的位数,分析题目可以知道k >= 2,所以如果n是由2进制构成的全1的字符串那么最多的位数应该是log2n向上取整位。所以我们可以枚举当前可能的位数,在枚举位数的时候那么m就确定了,但是我们怎么样确定k呢?分析题目可以知道k最大是n - 1,也即由2个1构成的字符串,如果暴力枚举的话肯定超时的,其实我们可以由上面的方程得到k大概的范围,因为枚举m的时候n,m都是确定的要想等式尽可能成立那么k一定是与n,m存在某种关系的,当我们得到k的范围之后那么剩下的工作就好办了,其中的证明过程如下。通过下面的证明我们可以得到k的具体值,通过计算当前m位k进制1是否等于n判断等式是否成立(暴力枚举的思想)。因为n一定的情况下m越大k一定越小,所以我们在枚举m的时候可以从大到小进行枚举,这样当我们找到答案之后对应的k肯定是最小的。
② 由①可知,当我们在枚举m位1的时候需要确定k,因为k越大那么对应的n是越大的所以我们可以使用二分来计算出当前k,在二分的时候计算k进制下m + 1位1对应的数字,判断是否等于n,两种做法都是基于暴力枚举的思想,都在尝试可能的m,k使得等式成立。
3. 代码如下:
数学:
import math
class Solution:
def smallestGoodBase(self, n: str) -> str:
n = int(n)
if n <= 3: return str(n - 1)
m_max = int(math.log(n, 2)) + 1
for m in range(m_max, 1, -1):
k = int(math.pow(n, 1.0 / m))
t = 0
# 计算k进制m + 1的1的结果
for j in range(m + 1):
t = t * k + 1
if t == n: return str(k)
return str(n - 1)
二分:
import math
class Solution:
def smallestGoodBase(self, n: str) -> str:
n = int(n)
if n <= 3: return str(n - 1)
m_max = int(math.log(n, 2)) + 1
for m in range(m_max, 1, -1):
l, r = 2, 10 ** 18
while l <= r:
k = l + r >> 1
t = 0
for j in range(m + 1):
t = t * k + 1
if t == n: return str(k)
if t > n:
r = k - 1
else:
l = k + 1
return str(n - 1)