Hard
题目描述
k 镜像数字是一个正整数(无前导零),在十进制和 k 进制下从前往后读和从后往前读都相同。
例如,9 是一个 2 镜像数字。9 在十进制和二进制下的表示分别是 9 和 1001,从前往后读和从后往前读都相同。
相反,4 不是一个 2 镜像数字。4 在二进制下的表示是 100,从前往后读和从后往前读不相同。
给定进制 k 和数字 n,返回前 n 个最小的 k 镜像数字的和。
示例 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
提示:
2 <= k <= 91 <= n <= 30
解题思路
解题思路
这道题要找前 n 个最小的 k 镜像数字,即在十进制和 k 进制下都是回文数的数字。
核心思路:
- 生成十进制回文数:按长度递增生成所有十进制回文数,而不是逐个检查每个数字
- 检查 k 进制回文性:对于每个十进制回文数,转换为 k 进制并检查是否也是回文
- 优化搜索空间:通过生成回文数而非暴力枚举大大减少搜索空间
生成回文数的方法:
- 对于长度为 d 的数字,可以通过取前 ⌈d/2⌉ 位作为"种子"
- 奇数长度回文:将种子的前 d/2 位反向拼接到种子后面
- 偶数长度回文:将种子的前 d/2 位反向拼接,不包括中间位
算法流程:
- 从长度 1 开始,逐个长度生成所有可能的十进制回文数
- 对每个回文数,转换为 k 进制字符串并检查是否回文
- 如果是 k 镜像数,加入结果并累加和
- 当找到 n 个数字时返回总和
这种方法比暴力枚举效率高很多,因为回文数的密度相对较低,直接生成可以避免大量无用检查。
代码实现
class Solution {
public:
long long kMirror(int k, int n) {
long long sum = 0;
int count = 0;
int len = 1;
while (count < n) {
// Generate palindromes of length len
long long start = (len == 1) ? 1 : pow(10, (len - 1) / 2);
long long end = pow(10, (len + 1) / 2);
for (long long i = start; i < end && count < n; i++) {
long long palindrome = generatePalindrome(i, len);
if (isPalindromeInBaseK(palindrome, k)) {
sum += palindrome;
count++;
}
}
len++;
}
return sum;
}
private:
long long generatePalindrome(long long seed, int len) {
string s = to_string(seed);
string palindrome = s;
if (len % 2 == 0) {
// Even length: reverse entire seed
reverse(s.begin(), s.end());
palindrome += s;
} else {
// Odd length: reverse seed without middle digit
reverse(s.begin(), s.end() - 1);
palindrome += s;
}
return stoll(palindrome);
}
bool isPalindromeInBaseK(long long num, int k) {
string baseK = "";
long long temp = num;
while (temp > 0) {
baseK = to_string(temp % k) + baseK;
temp /= k;
}
string reversed = baseK;
reverse(reversed.begin(), reversed.end());
return baseK == reversed;
}
};
class Solution:
def kMirror(self, k: int, n: int) -> int:
def generate_palindrome(seed, length):
s = str(seed)
if length % 2 == 0:
# Even length
return int(s + s[::-1])
else:
# Odd length
return int(s + s[-2::-1])
def is_palindrome_in_base_k(num, k):
base_k = ""
while num > 0:
base_k = str(num % k) + base_k
num //= k
return base_k == base_k[::-1]
total_sum = 0
count = 0
length = 1
while count < n:
# Generate palindromes of current length
start = 1 if length == 1 else 10 ** ((length - 1) // 2)
end = 10 ** ((length + 1) // 2)
for seed in range(start, end):
if count >= n:
break
palindrome = generate_palindrome(seed, length)
if is_palindrome_in_base_k(palindrome, k):
total_sum += palindrome
count += 1
length += 1
return total_sum
public class Solution {
public long KMirror(int k, int n) {
long sum = 0;
int count = 0;
int len = 1;
while (count < n) {
// Generate palindromes of length len
long start = (len == 1) ? 1 : (long)Math.Pow(10, (len - 1) / 2);
long end = (long)Math.Pow(10, (len + 1) / 2);
for (long i = start; i < end && count < n; i++) {
long palindrome = GeneratePalindrome(i, len);
if (IsPalindromeInBaseK(palindrome, k)) {
sum += palindrome;
count++;
}
}
len++;
}
return sum;
}
private long GeneratePalindrome(long seed, int len) {
string s = seed.ToString();
string palindrome = s;
if (len % 2 == 0) {
// Even length: reverse entire seed
char[] arr = s.ToCharArray();
Array.Reverse(arr);
palindrome += new string(arr);
} else {
// Odd length: reverse seed without middle digit
char[] arr = s.ToCharArray();
Array.Reverse(arr, 0, arr.Length - 1);
palindrome += new string(arr);
}
return long.Parse(palindrome);
}
private bool IsPalindromeInBaseK(long num, int k) {
string baseK = "";
long temp = num;
while (temp > 0) {
baseK = (temp % k).ToString() + baseK;
temp /= k;
}
char[] reversed = baseK.ToCharArray();
Array.Reverse(reversed);
return baseK == new string(reversed);
}
}
var kMirror = function(k, n) {
function isPalindrome(str) {
return str === str.split('').reverse().join('');
}
function toBaseK(num, base) {
if (num === 0) return '0';
let result = '';
while (num > 0) {
result = (num % base) + result;
num = Math.floor(num / base);
}
return result;
}
function generatePalindromes() {
const palindromes = [];
// Single digit palindromes
for (let i = 1; i <= 9; i++) {
palindromes.push(i);
}
// Multi-digit palindromes
for (let len = 2; len <= 10; len++) {
const isOdd = len % 2 === 1;
const halfLen = Math.floor(len / 2);
const start = Math.pow(10, halfLen - 1);
const end = Math.pow(10, halfLen);
for (let i = start; i < end; i++) {
const leftHalf = i.toString();
let rightHalf = leftHalf.split('').reverse().join('');
if (isOdd) {
for (let mid = 0; mid <= 9; mid++) {
const palindrome = parseInt(leftHalf + mid + rightHalf);
palindromes.push(palindrome);
}
} else {
const palindrome = parseInt(leftHalf + rightHalf);
palindromes.push(palindrome);
}
}
}
return palindromes.sort((a, b) => a - b);
}
const palindromes = generatePalindromes();
const kMirrors = [];
for (const num of palindromes) {
if (kMirrors.length >= n) break;
const baseKRep = toBaseK(num, k);
if (isPalindrome(baseKRep)) {
kMirrors.push(num);
}
}
return kMirrors.reduce((sum, num) => sum + num, 0);
};
复杂度分析
| 复杂度类型 | 值 |
|---|---|
| 时间复杂度 | O(m × log_k(P)) |
| 空间复杂度 | O(log_k(P)) |
其中 m 是生成的回文数数量,P 是第 n 个 k 镜像数的值。时间复杂度主要由生成回文数和检查 k 进制回文性决定。空间复杂度主要用于存储 k 进制字符串。
相关题目
. Strobogrammatic Number II (Medium)
. Prime Palindrome (Medium)