大家好,我是你们的算法小伙伴。今天我们来练习一道字符串处理的经典基础题 ------LeetCode 125. 验证回文串。这道题考察字符串的预处理和双指针的应用,难度适中,是面试中常见的字符串类入门题目,既能巩固基础语法,也能锻炼解题思路的严谨性。
题目描述
给定一个字符串s ,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入:s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:空字符串是回文串。
提示:
1 <= s.length <= 2 * 10⁵
字符串 s 由 ASCII 字符组成
解题思路
核心定义拆解
回文串的核心定义:正读和反读完全相同的字符串。本题有两个关键限制:
-
只考虑 字母和数字字符,忽略标点、空格等其他字符;
-
忽略字母的大小写(例如 'A' 和 'a' 视为相同字符)。
解题的核心思路的是:先对字符串进行预处理,过滤无效字符并统一大小写,再验证处理后的字符串是否为回文。
方法一:预处理 + 双指针
**思路:**先遍历原字符串,过滤掉非字母数字的字符,同时将所有字母转为统一大小写(小写或大写),得到一个纯字母数字的字符串;再用双指针从字符串两端向中间遍历,逐一对比对应位置的字符,若所有字符都相等,则为回文串。
方法二:原地双指针
**思路:**无需额外存储预处理后的字符串,直接在原字符串上使用双指针。左指针从头部、右指针从尾部开始,遇到非字母数字的字符则跳过,遇到字母则统一转为小写(或大写)后对比,直至指针相遇。该方法无需额外空间,效率更高。
代码实现
方法一:预处理 + 双指针
java
class Solution {
public boolean isPalindrome(String s) {
// 1. 预处理:过滤非字母数字字符,统一转为小写
StringBuilder filtered = new StringBuilder();
for (char c : s.toCharArray()) {
// 判断当前字符是否为字母或数字
if (Character.isLetterOrDigit(c)) {
filtered.append(Character.toLowerCase(c));
}
}
// 2. 双指针验证回文
int left = 0;
int right = filtered.length() - 1;
while (left < right) {
// 对比左右指针指向的字符
if (filtered.charAt(left) != filtered.charAt(right)) {
return false;
}
left++; // 左指针右移
right--; // 右指针左移
}
return true;
}
}
方法二:原地双指针(空间优化)
java
class Solution {
public boolean isPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
while (left < right) {
// 左指针跳过非字母数字字符
while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
left++;
}
// 右指针跳过非字母数字字符
while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
right--;
}
// 统一转为小写后对比
if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
return false;
}
left++;
right--;
}
return true;
}
}
代码详解
一、预处理 + 双指针
-
StringBuilder filtered = new StringBuilder();:用于存储预处理后的纯字母数字字符串,避免字符串拼接的性能损耗(String 不可变,拼接会产生新对象)。 -
Character.isLetterOrDigit(c):判断字符c是否为字母或数字,精准过滤标点、空格等无效字符。 -
Character.toLowerCase(c):将字母转为小写,消除大小写差异(例如 'A' 和 'a' 统一为 'a')。 -
双指针遍历:左指针从 0 开始(字符串头部),右指针从
filtered.length() - 1开始(字符串尾部),每次对比后指针向中间移动,若出现字符不相等则直接返回 false,直至指针相遇。
示例 1 模拟:
输入 "A man, a plan, a canal: Panama"
预处理后 → "amanaplanacanalpanama",双指针从两端对比,所有字符均相等,返回 true。
二、原地双指针
-
跳过无效字符:两个内层 while 循环分别控制左、右指针,遇到非字母数字字符则跳过,直至找到有效字符或指针相遇。
-
字符对比:找到有效字符后,统一转为小写对比,若不相等则返回 false;若相等则指针继续向中间移动。
-
优势:无需额外存储字符串,节省空间,尤其适合处理大规模字符串(如提示中长度达 2*10⁵ 的字符串)。
示例 2 模拟:
输入 "race a car"
原地遍历:左指针找到 'r',右指针找到 'r' → 相等;继续移动,左指针找到 'a',右指针找到 'a' → 相等;再移动,左指针找到 'c',右指针找到 'c' → 相等;继续移动,左指针找到 'e',右指针找到 'a' → 不相等,返回 false。
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 预处理 + 双指针 | O(n) | O(n) | 思路直观,代码简洁,易理解,不易出错 | 需要额外存储预处理后的字符串,占用空间 |
| 原地双指针 | O(n) | O(1) | 空间最优,适合大规模字符串,效率更高 | 需要处理指针跳过无效字符的逻辑,细节稍多 |
总结
验证回文串的核心:先过滤无效字符、统一大小写,再通过双指针验证对称性。
-
面试中,若不要求空间优化,优先写「预处理 + 双指针」解法,代码简洁、不易出错,能快速完成答题。
-
若面试官要求空间复杂度 O(1),则使用「原地双指针」解法,重点注意跳过无效字符的逻辑,避免指针越界。
-
本题的关键细节:忽略大小写、只考虑字母数字,这两个点容易遗漏,解题时需重点关注。
今天的每日算法练习就到这里,我们明天再见!👋