Medium
题目描述
给你一个整数数组 queries 和一个正整数 intLength,返回一个数组 answer,其中 answer[i] 是长度为 intLength 的第 queries[i] 小的正回文数,如果不存在这样的回文数,则为 -1。
回文数是正着读和倒着读都一样的数字。回文数不能有前导零。
示例 1:
输入:queries = [1,2,3,4,5,90], intLength = 3
输出:[101,111,121,131,141,999]
解释:
长度为 3 的前几个回文数是:
101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, ...
第 90 个长度为 3 的回文数是 999。
示例 2:
输入:queries = [2,4,6], intLength = 4
输出:[1111,1331,1551]
解释:
长度为 4 的前 6 个回文数是:
1001, 1111, 1221, 1331, 1441, 1551。
约束条件:
1 <= queries.length <= 5 * 10^41 <= queries[i] <= 10^91 <= intLength <= 15
提示:
- 对于任意的
queries[i]和intLength值,你如何检查是否至少存在queries[i]个长度为intLength的回文数? - 由于回文数正着读和倒着读都一样,考虑如何有效地找到回文数的前一半(
ceil(intLength/2)位数字)。
解题思路
解题思路
这道题的关键洞察是:回文数由其前一半数字完全确定,后一半是前一半的镜像。
核心思路:
- 对于长度为
n的回文数,我们只需要确定前ceil(n/2)位数字 - 例如长度为 5 的回文数
12321,只需要确定前 3 位123,后面的21是12的镜像 - 长度为 4 的回文数
1221,只需要确定前 2 位12,后面的21是12的镜像
算法步骤:
- 计算前一半的长度:
halfLen = ceil(intLength / 2) - 计算前一半可能的数字范围:从
10^(halfLen-1)到10^halfLen - 1 - 总的回文数个数为:
10^halfLen - 10^(halfLen-1) - 如果查询的序号超过总数,返回 -1
- 否则,找到对应的前一半数字,然后构造完整的回文数
构造回文数:
- 奇数长度:前一半 + 去掉最后一位的前一半的逆序
- 偶数长度:前一半 + 前一半的逆序
时间复杂度 O(q × intLength),其中 q 是查询次数,空间复杂度 O(1)。
代码实现
class Solution {
public:
vector<long long> kthPalindrome(vector<int>& queries, int intLength) {
vector<long long> result;
// 计算前一半的长度
int halfLen = (intLength + 1) / 2;
// 前一半的最小值和最大值
long long halfStart = pow(10, halfLen - 1);
long long halfEnd = pow(10, halfLen) - 1;
// 总的回文数个数
long long totalCount = halfEnd - halfStart + 1;
for (int query : queries) {
if (query > totalCount) {
result.push_back(-1);
continue;
}
// 找到对应的前一半
long long halfNum = halfStart + query - 1;
// 构造回文数
string halfStr = to_string(halfNum);
string palindrome = halfStr;
// 根据长度奇偶性添加后一半
if (intLength % 2 == 0) {
// 偶数长度:直接镜像
reverse(halfStr.begin(), halfStr.end());
palindrome += halfStr;
} else {
// 奇数长度:去掉中间数字后镜像
reverse(halfStr.begin(), halfStr.end());
palindrome += halfStr.substr(1);
}
result.push_back(stoll(palindrome));
}
return result;
}
};
class Solution:
def kthPalindrome(self, queries: List[int], intLength: int) -> List[int]:
result = []
# 计算前一半的长度
half_len = (intLength + 1) // 2
# 前一半的最小值和最大值
half_start = 10 ** (half_len - 1)
half_end = 10 ** half_len - 1
# 总的回文数个数
total_count = half_end - half_start + 1
for query in queries:
if query > total_count:
result.append(-1)
continue
# 找到对应的前一半
half_num = half_start + query - 1
# 构造回文数
half_str = str(half_num)
if intLength % 2 == 0:
# 偶数长度:直接镜像
palindrome = half_str + half_str[::-1]
else:
# 奇数长度:去掉中间数字后镜像
palindrome = half_str + half_str[-2::-1]
result.append(int(palindrome))
return result
public class Solution {
public long[] KthPalindrome(int[] queries, int intLength) {
long[] result = new long[queries.Length];
// 计算前一半的长度
int halfLen = (intLength + 1) / 2;
// 前一半的最小值和最大值
long halfStart = (long)Math.Pow(10, halfLen - 1);
long halfEnd = (long)Math.Pow(10, halfLen) - 1;
// 总的回文数个数
long totalCount = halfEnd - halfStart + 1;
for (int i = 0; i < queries.Length; i++) {
if (queries[i] > totalCount) {
result[i] = -1;
continue;
}
// 找到对应的前一半
long halfNum = halfStart + queries[i] - 1;
// 构造回文数
string halfStr = halfNum.ToString();
string palindrome = halfStr;
// 根据长度奇偶性添加后一半
if (intLength % 2 == 0) {
// 偶数长度:直接镜像
char[] chars = halfStr.ToCharArray();
Array.Reverse(chars);
palindrome += new string(chars);
} else {
// 奇数长度:去掉中间数字后镜像
char[] chars = halfStr.ToCharArray();
Array.Reverse(chars);
palindrome += new string(chars, 1, chars.Length - 1);
}
result[i] = long.Parse(palindrome);
}
return result;
}
}
/**
* @param {number[]} queries
* @param {number} intLength
* @return {number[]}
*/
var kthPalindrome = function(queries, intLength) {
const result = [];
const halfLen = Math.ceil(intLength / 2);
const start = Math.pow(10, halfLen - 1);
const end = Math.pow(10, halfLen) - 1;
const maxPalindromes = end - start + 1;
for (let query of queries) {
if (query > maxPalindromes) {
result.push(-1);
continue;
}
const halfNum = start + query - 1;
const halfStr = halfNum.toString();
let palindrome;
if (intLength % 2 === 0) {
palindrome = halfStr + halfStr.split('').reverse().join('');
} else {
palindrome = halfStr + halfStr.slice(0, -1).split('').reverse().join('');
}
result.push(parseInt(palindrome));
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(q × intLength) | q 为查询次数,每次查询需要 O(intLength) 时间构造回文数字符串 |
| 空间复杂度 | O(1) | 除了结果数组外,只使用常数额外空间 |