题目
编写程序,检测给定的字符串是否为回文字符串(Palindrome)。要求忽略所有非字母和非数字的标点空格,且对大小写不敏感(例如:"A man, a plan, a canal: Panama" 应被判定为合法回文)。采用双指针从两端向中间对向收敛逼近,算法额外空间复杂度严格为 O(1),无需额外申请临时拷贝串。
输入格式:一行可能包含大小写、空格与标点符号的文本。
输出格式:输出该文本是否为合法回文串的布尔判定结果。
数据范围与约定:文本长度 <code>≤ 10^5</code>,纯 ASCII 字符。
思路与算法
1. 双指针 O(1) 空间优势
初学者常使用 std::reverse 构造新串再比对,消耗 O(N) 堆内存。左右双指针(left 与 right)就地向中心扫描,遇到非字母数字字符则单侧跳过,完全零额外内存。
2. 标准库字符分类
使用 std::isalnum(static_cast<unsigned char>(c)) 过滤有效字符;使用 std::tolower 统一字符规范。
3. 指针越界交叉防御
在 while 循环跳过标点时,内层循环必须始终加上 left < right 条件限制,防止在全标点极端输入下两个指针交叉越界产生越界访问。
完整程序
#include <iostream>
#include <string>
#include <cctype>
bool isPalindrome(const std::string& s) {
if (s.empty()) return true;
int left = 0;
int right = static_cast<int>(s.size()) - 1;
while (left < right) {
// 1. 左指针向右寻找下一个字母数字
while (left < right && !std::isalnum(static_cast<unsigned char>(s[left]))) {
++left;
}
// 2. 右指针向左寻找上一个字母数字
while (left < right && !std::isalnum(static_cast<unsigned char>(s[right]))) {
--right;
}
// 3. 统一转小写比对
if (std::tolower(static_cast<unsigned char>(s[left])) !=
std::tolower(static_cast<unsigned char>(s[right]))) {
return false;
}
++left;
--right;
}
return true;
}
int main() {
std::string line;
if (!std::getline(std::cin, line)) {
return 0;
}
bool res = isPalindrome(line);
std::cout << "测试文本: [\"" << line << "\"]\n";
std::cout << "回文检测结论: " << (res ? "【是回文字符串】" : "【不是回文字符串】") << "\n";
return 0;
}运行示例
示例 1:
输入:
A man, a plan, a canal: Panama输出:
测试文本: ["A man, a plan, a canal: Panama"]
回文检测结论: 【是回文字符串】
示例 2:
输入:
race a car输出:
测试文本: ["race a car"]
回文检测结论: 【不是回文字符串】