Medium
题目描述
给你一个回文字符串 s。
返回 s 的字典序最小的回文排列。
示例 1:
输入:s = "z"
输出:"z"
解释:
只有一个字符的字符串本身就是字典序最小的回文。
示例 2:
输入:s = "babab"
输出:"abbba"
解释:
重新排列 "babab" → "abbba" 得到最小的字典序回文。
示例 3:
输入:s = "daccad"
输出:"acddca"
解释:
重新排列 "daccad" → "acddca" 得到最小的字典序回文。
约束条件:
1 <= s.length <= 10^5s由小写英文字母组成s保证是回文的
提示:
- 将回文看作由两个镜像对称的半部分组成
- 构造一半(使用 s),然后另一半是它的反转,以获得字典序最小的排列
解题思路
这道题要求我们找到给定回文字符串的字典序最小的回文排列。
核心思路:
由于题目保证输入字符串是回文的,我们可以利用回文的对称性质来解决。回文字符串可以分为两部分:左半部分和右半部分(如果长度为奇数,中间还有一个字符)。
解题步骤:
- 统计字符频次:首先统计每个字符出现的次数
- 构造左半部分:为了得到字典序最小的结果,我们按字典序遍历所有字符,对于每个字符,将其一半数量放入左半部分
- 处理中间字符:如果某个字符的出现次数是奇数,那么多出来的一个字符应该放在回文的中心位置
- 构造右半部分:右半部分是左半部分的反转
算法优化:
由于我们只需要构造左半部分,然后镜像得到右半部分,时间复杂度可以优化到 O(n),其中 n 是字符串长度。我们可以直接统计字符频次,然后按字典序构造结果。
这种方法确保了结果的字典序最小,因为我们总是优先放置字典序较小的字符在前面的位置。
代码实现
class Solution {
public:
string smallestPalindrome(string s) {
vector<int> count(26, 0);
for (char c : s) {
count[c - 'a']++;
}
string left = "";
string middle = "";
for (int i = 0; i < 26; i++) {
if (count[i] > 0) {
char c = 'a' + i;
left += string(count[i] / 2, c);
if (count[i] % 2 == 1) {
middle = c;
}
}
}
string right = left;
reverse(right.begin(), right.end());
return left + middle + right;
}
};
class Solution:
def smallestPalindrome(self, s: str) -> str:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
left = []
middle = ""
for i in range(26):
if count[i] > 0:
c = chr(i + ord('a'))
left.append(c * (count[i] // 2))
if count[i] % 2 == 1:
middle = c
left_str = ''.join(left)
right_str = left_str[::-1]
return left_str + middle + right_str
public class Solution {
public string SmallestPalindrome(string s) {
int[] count = new int[26];
foreach (char c in s) {
count[c - 'a']++;
}
StringBuilder left = new StringBuilder();
string middle = "";
for (int i = 0; i < 26; i++) {
if (count[i] > 0) {
char c = (char)('a' + i);
left.Append(new string(c, count[i] / 2));
if (count[i] % 2 == 1) {
middle = c.ToString();
}
}
}
StringBuilder right = new StringBuilder();
for (int i = left.Length - 1; i >= 0; i--) {
right.Append(left[i]);
}
return left.ToString() + middle + right.ToString();
}
}
/**
* @param {string} s
* @return {string}
*/
var smallestPalindrome = function(s) {
const freq = {};
for (const char of s) {
freq[char] = (freq[char] || 0) + 1;
}
const left = [];
let middle = '';
for (const char of 'abcdefghijklmnopqrstuvwxyz') {
if (freq[char]) {
const pairs = Math.floor(freq[char] / 2);
for (let i = 0; i < pairs; i++) {
left.push(char);
}
if (freq[char] % 2 === 1 && middle === '') {
middle = char;
}
}
}
return left.join('') + middle + left.reverse().join('');
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历字符串一次统计频次,构造结果字符串的时间也是 O(n) |
| 空间复杂度 | O(1) | 使用固定大小的数组统计字符频次,不考虑结果字符串的空间 |
相关题目
- . Shortest Palindrome (Hard)