字典序最小回文串的算法解析
题目概述
给定一个由小写英文字母组成的字符串s
,要求通过最少的字符替换次数,将其转换为一个字典序最小的回文串。回文串是指正着读和反着读都一样的字符串。
解题思路
核心思路
核心思路是将字符串s
的前半部分与后半部分进行比较,并根据字典序进行适当的字符替换,以达到回文串的要求。
算法步骤
- 计算字符串
s
的长度n
。 - 遍历字符串
s
的前半部分,即遍历从开始到n/2
的位置。 - 在每次遍历中,比较当前位置
i
的字符left
和对称位置n-i-1
的字符right
。- 如果
left
大于right
,说明为了保持字典序最小,应该将left
替换为right
。 - 否则,将
right
替换为left
。
- 如果
- 组合修改后的字符序列,返回结果。
代码注释
Python 版本
class Solution:
def makeSmallestPalindrome(self, s: str) -> str:
n = len(s) # 计算字符串长度
s = list(s) # 转换为字符列表,方便修改
for i in range(n // 2): # 遍历字符串的前半部分
left = s[i] # 获取当前位置的字符
right = s[-1 - i] # 获取对称位置的字符
if left != right: # 如果两个字符不相等
if left > right: # 如果左边字符的字典序大于右边
s[i] = right # 替换左边字符
else:
s[-1 - i] = left # 否则替换右边字符
return ''.join(s) # 返回修改后的字符串
Java 版本
class Solution {
public String makeSmallestPalindrome(String s) {
char[] char_s = s.toCharArray(); // 将字符串转换为字符数组
for (int i = 0, j = s.length() - 1; i < s.length() / 2; i++, j--) {
char left = char_s[i], right = char_s[j]; // 获取当前和对称位置的字符
if (left > right) {
char_s[i] = right; // 字典序大的字符替换为小的
} else {
char_s[j] = left; // 反之亦然
}
}
return new String(char_s); // 返回结果字符串
}
}
C++ 版本
class Solution {
public:
string makeSmallestPalindrome(string s) {
int n = s.length(); // 字符串长度
for (int i = 0, j = n - 1; i < n / 2; i++, j--) {
char left = s[i], right = s[j]; // 获取当前和对称位置的字符
if (left > right) {
s[i] = right; // 字典序大的字符替换为小的
} else {
s[j] = left; // 反之亦然
}
}
return s; // 返回结果字符串
}
};
Go 版本
func makeSmallestPalindrome(s string) string {
char_s := []byte(s) // 将字符串转换为字节切片
for i, j := 0, len(s) - 1; i < len(s) / 2; i, j = i + 1, j - 1 {
left, right := s[i], s[j] // 获取当前和对称位置的字符
if left > right {
char_s[i] = right // 字典序大的字符替换为小的
} else {
char_s[j] = left //
反之亦然
}
}
return string(char_s) // 返回结果字符串
}
TypeScript 版本
function makeSmallestPalindrome(s: string): string {
const char_s = s.split(''); // 将字符串分割为字符数组
for (let i = 0, j = s.length - 1; i < j; i++, j--) {
let left = char_s[i], right = char_s[j]; // 获取当前和对称位置的字符
if (left > right) {
char_s[i] = right; // 字典序大的字符替换为小的
} else {
char_s[j] = left; // 反之亦然
}
}
return char_s.join(''); // 返回结果字符串
};
JavaScript 版本
/**
* @param {string} s
* @return {string}
*/
var makeSmallestPalindrome = function(s) {
let n = s.length; // 字符串长度
for (let i = 0, j = n - 1; i < j; i++, j--) {
let left = s[i], right = s[j]; // 获取当前和对称位置的字符
if (left > right) {
s[i] = right; // 字典序大的字符替换为小的
} else {
s[j] = left; // 反之亦然
}
}
return s; // 返回结果字符串
};
Rust 版本
impl Solution {
pub fn make_smallest_palindrome(s: String) -> String {
let mut char_s: Vec<char> = s.chars().collect(); // 将字符串转换为字符向量
let n = char_s.len(); // 字符串长度
for i in 0..n / 2 {
let j = n - 1 - i;
let left: char = char_s[i];
let right: char = char_s[j];
if left > right {
char_s[i] = right; // 字典序大的字符替换为小的
} else {
char_s[j] = left; // 反之亦然
}
}
char_s.into_iter().collect() // 返回结果字符串
}
}
Rust代码解读与Rust特性分析
-
类型安全与明确性:
Rust 强调类型安全和明确性,这在函数签名中体现得淋漓尽致。例如,pub fn make_smallest_palindrome(s: String) -> String
明确指出函数接受一个String
类型的参数并返回一个String
类型的结果。 -
所有权与借用机制:
在 Rust 中,所有权(ownership)和借用(borrowing)机制是核心概念。本例中,函数接受一个属于调用者的String
类型参数。一旦传递给函数,所有权就转移了,保证了内存安全和有效管理。 -
迭代器的使用:
代码s.chars().collect()
使用迭代器将字符串转换为字符向量。Rust 中的迭代器是一种高效处理集合数据的工具,支持多种操作,如映射、过滤等,此处用于字符转换。 -
向量处理:
代码定义了一个向量char_s
存储字符。Rust 中的向量(Vec)是一个动态数组,可以高效地增长和缩短,非常适合用于存储未知数量的元素。 -
借用与可变性:
let mut char_s
指出char_s
是一个可变借用,意味着可以修改其中的元素。Rust 通过可变性控制来提供更细粒度的内存管理。 -
遍历与条件判断:
循环for i in 0..n / 2
演示了 Rust 的遍历机制,其中使用范围表达式进行半长度遍历。结合条件判断,实现了字符间的比较和相应的替换逻辑。 -
模式匹配与替换:
通过比较left
和right
字符,进行条件替换。这种模式匹配是 Rust 中强大的控制流工具之一,使代码更加清晰和健壯。 -
结果的构建:
最后,char_s.into_iter().collect()
将修改后的字符向量重新组装成一个字符串。这体现了 Rust 的迭代器和集合框架的灵活性和强大功能。
结论
通过上述代码实现,我们可以看到,不同编程语言在处理字符串和字符的方式上有所不同,但核心算法思想是一致的。这个问题通过简单的字符比较和替换,就能够有效地实现回文串的转换,并保证字典序最小。