1. 题目
题目描述3
给定一个字符串 s ,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
本题中,将空字符串定义为有效的回文串。
示例
输入:s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文
输入:s = "race a car"
输出:false
输入:s = " "
输出:true
约束
-
(1 <= s.length < 2 *10^5)
-
s 仅由可打印的 ASCII 字符组成
2. 最佳解题思路描述(原地双指针,O (1) 空间最优)
-
左指针
l从头部、右指针r从尾部向中间靠拢; -
左指针跳过所有非字母数字字符,右指针同理;
-
左右指针都定位到有效字符后,统一转为小写比较:
-
不相等 → 直接返回 false;
-
相等 → l++、r--,继续向内收缩;
-
-
两指针相遇 / 交叉说明全部匹配,返回 true。
优势
无需新建过滤后的字符串,原地操作,空间复杂度 (O(1));一次遍历即可完成,效率更高。
3. 我的代码(逻辑正确可 AC,但额外开辟字符串,空间非最优)
class Solution {
public:
bool isPalindrome(string s) {
string s1;
for(int i = 0;i<s.size();i++){
if(s[i]>='A'&&s[i]<='Z'){
s1+=s[i]+32;
}
if(s[i]>='a'&&s[i]<='z'){
s1+=s[i];
}
if(s[i]>='0'&&s[i]<='9'){
s1+=s[i];
}
}
cout<<s1<<endl;
int len = s1.size();
bool flag = true;
for(int i = 0;i<len/2;i++){
if(s1[i]!=s1[len-i-1]){
flag = false;
break;
}
}
return flag;
}
};
代码说明
-
正确性:逻辑无 bug,能通过全部测试用例;
-
存在缺陷与优化点:
-
额外创建字符串
s1存储过滤、小写后的字符,空间 (O(n)),大数据量占用更多内存; -
存在调试输出
cout<<s1<<endl;,提交前必须删除,否则判题会输出多余内容报错; -
判断字符区间写法繁琐,可使用库函数
isalnum()一键判断是否为字母数字; -
大写转小写
+32依赖 ASCII 编码,可读性差,标准函数tolower()更规范; -
变量
flag可省略,字符不等直接 return false 提前终止循环。
-
4. 最优标准代码(原地左右双指针,O (1) 空间)
#include <cctype>
class Solution {
public:
bool isPalindrome(string s) {
int l = 0, r = s.size() - 1;
while (l < r) {
// 左指针跳过非字母数字
while (l < r && !isalnum(s[l])) l++;
// 右指针跳过非字母数字
while (l < r && !isalnum(s[r])) r--;
// 统一转小写对比
if (tolower(s[l]) != tolower(s[r])) {
return false;
}
l++;
r--;
}
return true;
}
};
5. 总结
-
你的过滤新建字符串写法思路简单、容易理解,但空间开销大,面试优先使用双指针原地方案;
-
关键工具函数:
isalnum(c)判断是否字母 / 数字,tolower(c)统一小写; -
必须删除调试用 cout 输出,否则在线判题会出错;
-
回文判断核心:对称位置字符忽略大小写相等,非字母数字直接忽略。
6. 相关知识拓展
拓展 1:基于原代码的优化版本(保留过滤字符串,去除冗余)
#include <cctype>
class Solution {
public:
bool isPalindrome(string s) {
string t;
for (char c : s) {
if (isalnum(c)) {
t += tolower(c);
}
}
int n = t.size();
for (int i = 0; i < n / 2; ++i) {
if (t[i] != t[n - i - 1]) return false;
}
return true;
}
};
拓展 2:字符判断底层原理
-
isalnum(c):返回 true 当且仅当 c 是大小写字母或数字; -
大写字母 ASCII 比对应小写小 32,
A-Z +32 = a-z,仅字母适用,数字不能加减 32 转换。
拓展 3:复杂度对比
-
过滤新建字符串(你的写法):时间 (O(n)),空间 (O(n));
-
原地双指针最优解:时间 (O(n)),空间 (O(1));
拓展 4:易错点复盘
-
忘记过滤符号、空格,直接对比原字符串字符;
-
大小写未统一,导致 'A' 和 'a' 判定不相等;
-
保留 cout 调试输出,在线 OJ 判题失败;
-
双指针内层跳过字符时忘记加
l<r边界,指针越界。