2081-k 镜像数字的和
一个 k 镜像数字 指的是一个在十进制和 k 进制下从前往后读和从后往前读都一样的 没有前导 0 的 正 整数。
- 比方说,
9
是一个 2 镜像数字。9
在十进制下为9
,二进制下为1001
,两者从前往后读和从后往前读都一样。 - 相反地,
4
不是一个 2 镜像数字。4
在二进制下为100
,从前往后和从后往前读不相同。
给你进制 k
和一个数字 n
,请你返回 k 镜像数字中 最小 的 n
个数 之和 。
示例 1:
**输入:** k = 2, n = 5
**输出:** 25
**解释:** 最小的 5 个 2 镜像数字和它们的二进制表示如下:
十进制 二进制
1 1
3 11
5 101
7 111
9 1001
它们的和为 1 + 3 + 5 + 7 + 9 = 25 。
示例 2:
**输入:** k = 3, n = 7
**输出:** 499
**解释:** 7 个最小的 3 镜像数字和它们的三进制表示如下:
十进制 三进制
1 1
2 2
4 11
8 22
121 11111
151 12121
212 21212
它们的和为 1 + 2 + 4 + 8 + 121 + 151 + 212 = 499 。
示例 3:
**输入:** k = 7, n = 17
**输出:** 20379000
**解释:** 17 个最小的 7 镜像数字分别为:
1, 2, 3, 4, 5, 6, 8, 121, 171, 242, 292, 16561, 65656, 2137312, 4602064, 6597956, 6958596
提示:
2 <= k <= 9
1 <= n <= 30
方法一:折半搜索
思路与算法
最容易想到的办法是从 1 开始递增地依次搜索每一个数。当我们搜索到数 i 时,如果 i 是回文的,并且 i 的 k 进制表示也是回文的,那么我们就将 i 加入答案。当我们搜索到 n 个符合要求的数后,就可以停止搜索并返回答案。
但这样搜索会超出时间限制。当 k = 7 时,第 30 个满足要求的数为 64454545446 \approx 6 \times 10^{10。即使对于每个数我们可以 O(1) 判断其是否符合要求,搜索到 64454545446 需要的时间也是远远大于时间限制的。
因此我们可以考虑进行「折半」搜索。因为 i 本身是回文的,我们可以搜索 i 的一半 i’,然后将 i’ 翻转并且拼接在 i’ 的后面,这样就得到了 i。这样「折半」搜索的好处在于:我们得到的 i 一定是回文的,这样大大减少了搜索空间,例如在所有小于等于 10^{10 的数中,原来的搜索方法需要搜索全部的 10^{10 个数,而使用「折半」搜索只需要搜索 O(\sqrt{10^{10} }) = O(10^5) 个回文数。
在将 i’ 拼接成 i 时,需要注意有两种拼接方法,即回文数的长度有奇有偶。例如当 i’ = 123 时,可以拼接得到 12321 或者 123321,具体取决于是将 i’ 的个位数作为奇数长度回文数的回文中心,还是直接将 i’ 翻转并拼接,得到一个长度为偶数的回文数。
为了递增地搜索 i,我们同样需要递增地搜索 i’。对于同一个 i’,偶数长度的回文数的值一定大于奇数长度的回文数的值,因此我们需要按照如下的方法进行搜索:
我们规定搜索的范围,i’ 需要在 [10^k, 10^{k+1}) 的范围内;
我们在该范围内递增枚举 i’,通过 i’ 构造出奇数长度的回文数,并判断其是否符合要求;
我们在该范围内递增枚举 i’,通过 i’ 构造出偶数长度的回文数,并判断其是否符合要求。
这样一来,我们就可以保证 i 是递增搜索的。
代码
1 | class Solution { |
1 | class Solution: |
复杂度分析
对于给定的 n 和 k,我们很难找出第 n 个 k 镜像数字的范围。对于本题而言,最坏情况下为 n = 30,k = 7,对应的数为 64454545446,因此我们期望搜索的范围为 O(10^5)。
方法二:预处理
思路与算法
我们可以预处理出 k = 2, 3, \cdots, 9 的前 30 个 k 镜像数字,并直接累加返回答案。
代码
1 | class Solution { |
1 | class Solution: |