题目描述
只考虑字母和数字、忽略大小写,判断字符串是否为回文。
思路解析
最直接的想法:先 for 一遍过滤掉标点、转小写造出 "abcba",再反转比较。能做,但白白多扫一遍、多开一个字符串。
转折:不必造新串。l 从左、r 从右同时向里走,谁指向标点就只移谁、不比较(这就是负例动作);两边都落在字母数字上时才转小写比一对。相遇即说明全程对称。O(1) 额外空间。
准备 · 首尾出发:左指针 l=0 指 A,右指针 r=6 指 a。开始向中间收。
对比① · 看两端:两端都是字母,转小写:'A'→'a'、'a'→'a'。比一下。
对比① · 相等 → 内收:'a' == 'a',这一对通过,l 右移、r 左移。
l=1 是 '?' · 跳过(负例):l 指向 '?',不是字母数字。不比较,只让 l 前进到 2——r 原地不动。这就是「跳过杂质」的负例动作。
r=5 是 '?' · 跳过(负例):r 也指向 '?',同样只退 r 到 4、不比较。现在 l=2、r=4 都落在字母上了。
对比② · 看两端:l=2 是 'b'、r=4 是 'b',两边都落在字母上,可以比了。
对比② · 相等 → 内收:'b' == 'b',通过!内收:l→3、r→3。
l == r · 相遇 → true:l、r 在中间的 C 相遇(中点字符自己和自己对称,无需比)。全程每一对都相等,返回 true。
回文类问题的通用套路:左右指针向中间走、逐对比较;遇到要忽略的字符就只移动那侧的指针。
参考代码(Python)
1l, r = 0, len(s) - 1
2while l < r:
3 while l < r and not s[l].isalnum(): l += 1 # 跳左杂质
4 while l < r and not s[r].isalnum(): r -= 1 # 跳右杂质
5 if s[l].lower() != s[r].lower(): return False
6 l += 1; r -= 1
7return True复杂度分析
- 时间复杂度:O(n) —— 每个字符最多看一次
- 空间复杂度:O(1) —— 原地双指针
套路模板
记住骨架:两端向内、跳过无关项、规范化后比较、不等即假。改一下「跳过」和「规范化」规则就能套各种回文判定。
1l, r = 0, n - 1
2while l < r:
3 while l < r and 该跳过(s[l]): l += 1
4 while l < r and 该跳过(s[r]): r -= 1
5 if 规范化(s[l]) != 规范化(s[r]): return False
6 l += 1; r -= 1
7return True易错点
- 错误写法:内层跳过时不判 l < r → 正确写法:while 里始终带 l < r(全是杂质时指针会越界)
- 错误写法:比较忘了统一大小写 → 正确写法:都 .lower() 再比(题目忽略大小写)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。