LeetCode 补充题 137. 区分大小写与标点的回文判定
题目描述
:::fold-green 相关原题
LeetCode 原题: ✅ 125. 验证回文串
LeetCode 原题忽略大小写和非字母数字字符,输入为 ASCII 字符串;本文按 Unicode 码点比较,保留大小写、空格和标点。
:::
给你一个字符串
s,请判断它的字符正序与倒序是否完全一致,并返回布尔值。比较以 Unicode 码点为单位,大小写、空格和标点都参与比较,不能忽略或转换这些字符。空字符串返回
true。
示例 1:
输入:
s = "Aa"
输出:false
解释: 大小写参与比较,A 与 a 不相同。
示例 2:
输入:
s = "a,b,a"
输出:true
解释: 字符从两端向中间逐一对应,逗号也参与比较。
示例 3:
输入:
s = "😀a😀"
输出:true
解释: 两端都是同一个 Unicode 码点,中间字符为 a。
提示:
- 按 Unicode 码点逐一比较,大小写、空格和标点均参与。
- 空串返回
true。 - Go 输入约定为合法 UTF-8。
题意分析
本题不做字符过滤或大小写归一化,所有码点都要成对相等。需要注意编码长度与字符数量不同:一个码点可能占多个 UTF-8 字节或两个 UTF-16 单元,不能直接逐字节反转比较。
解法:双指针按 Unicode 码点比较
核心思路
[!blue]
维护尚未比较的半开区间
[left,right)。分别读取左端的下一个码点、右端之前的最后一个码点;不相等就已有一对对称字符冲突,可以立即返回 false。两端相等时,按各自实际编码宽度推进边界。Java 用
codePointAt、codePointBefore读取并用charCount移动;Go 从 UTF-8 两端解码,使用返回的字节数移动,始终让边界落在码点之间。区间外的对称位置已经全部相等。当边界相遇或交错,剩余至多一个中间码点,不会产生冲突,返回 true;空串也自然满足这一条件。
解题步骤
- 左端指向字符串开头,右端指向字符串末尾之后。
- 分别解码两端码点并比较,不同立即返回 false。
- 按各自编码宽度推进两端,直到相遇或交错后返回 true。
代码实现
class Solution {
public boolean exactPalindrome(String s) {
int left = 0;
int right = s.length();
while (left < right) {
int a = s.codePointAt(left);
int b = s.codePointBefore(right);
if (a != b) {
return false;
}
left += Character.charCount(a);
right -= Character.charCount(b);
}
return true;
}
}
import "unicode/utf8"
func exactPalindrome(s string) bool {
left, right := 0, len(s)
for left < right {
a, na := utf8.DecodeRuneInString(s[left:right])
b, nb := utf8.DecodeLastRuneInString(s[left:right])
if a != b {
return false
}
left += na
right -= nb
}
return true
}
复杂度分析
- 时间复杂度:$O(n)$。
- 空间复杂度:额外空间 $O(1)$。
关键点总结
[!green]
比较单位是码点,指针移动单位却是 UTF-16 单元或 UTF-8 字节;每次必须按实际编码宽度移动。
易错点总结
[!yellow]
不能删掉标点、空格或转成小写;Go输入约定为合法UTF-8。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 125. 验证回文串 | 简单 | 本题保留大小写、空格和标点,不能复用原题过滤字符与转小写的步骤。 |
| 680. 验证回文串 II | 简单 | 允许删除一个字符时,失配处分成跳过左端或右端两个回文检查,复用两端比较过程。 |