当前:LC125 · 验证回文串 · 首次出现于 Day 4 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC125 · Valid Palindrome · 双指针

验证回文串:跳过杂质,两头对碰

只比字母和数字,大小写当相同。指针停在逗号或空格上不算失败,先跳过再比。

有效字符是字母与数字,比较前统一成小写。可以先清洗成新串再双指针,也可以在原串上左右夹,遇到非字母数字就移动该侧。一旦两有效字符不等返回 false,指针交错则 true。时间 O(n);先清洗额外 O(n) 空间,原地跳过是 O(1)。

时间 O(n)空间 O(n)结论先行 · 全文约 6 节
导读

这是 LeetCode 125. Valid Palindrome。判断字符串是不是回文:只看字母和数字,忽略大小写,空格、标点、其它符号都不参与比较。空串、洗完为空,都算回文。

主例 "A man, a plan, a canal: Panama" 是回文——洗完是 amanaplanacanalpanama,正反相同。对照 "race a car" 洗完 racecar 差一个字母,中间的 e 对不上 a,不是回文。

直接把原串当普通回文去夹,会在第一对 "A" 和 "a" 之前先撞上冒号和空格,误判失败。缺的是「什么才算一对」。本文先把有效字符提出来,再两头对碰;原地做法只是把清洗折进移动指针的过程。

先定「有效字符」

题目把回文的字母表收窄了:unicode 字母、十进制数字算有效,其余跳过;大写与小写视为同一个字符。主例里的空格、逗号、冒号全部丢掉,"A" 和 "P" 先转成 "a" 和 "p"。

第一直觉是先建一个只含有效小写字符的新串。主例洗完是 amanaplanacanalpanama,长度 21,演示两帧从原句到这串。空间 O(n),但「要比的序列」变得看得见,后面的双指针不再分叉。

若要求额外空间 O(1),不要建新串:左右指针在原串上走,每次先 while 跳过无效字符,再比 ToLower 之后的值。判定规则与先清洗完全相同,只是跳过发生在比较之前的那一刻。

清洗:只留字母数字,转小写
原文
A man, a plan, a canal: Panama

双指针:两端对碰

清洗之后,left 在 0,right 在最后一格。每一对只问 s[left] 是否等于 s[right]:不等立刻 false;相等则 left++、right--。left ≥ right 时中间已经空或只剩一个字符,回文成立。

回文的本质是沿中点对称。从两端成对验证,比先反转再比整串少一次分配,失败也可以提前停。主例长度 21,中点是下标 10 的 c,它没有配对义务。

用手走主例,对应三帧。第一对下标 0 与 20,a 对 a。跳过中间若干对之后,演示停在 5 与 15,仍是 p 对 p。再收到 9 与 11,n 对 n,下一步 left 与 right 交错,判定 true。"race a car" 洗成 raceacar,第一对 r 对 r,收到 c 对 a 时失败。

对称回文就是沿中点对称。双指针从两端对碰,每步验证一对对称位置。杂质必须先从这对位置里拿掉,否则比的不是题目要的字符。
left 与 right 逐对比对,向中间靠拢
清洗后
amanaplanacanalpanama
原文:amanaplanacanalpanama
a」==「a」→ 继续

九行 Go:清洗 + 双指针

solution.goGo
func isPalindrome(s string) bool {
t := make([]rune, 0, len(s))
for _, r := range s {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
t = append(t, unicode.ToLower(r))
}
}
for i, j := 0, len(t)-1; i < j; i, j = i+1, j-1 {
if t[i] != t[j] { return false }
}
return true
}

1按 rune 扫,避免把多字节字母拆开。只收 IsLetter 或 IsDigit。

2收进来立刻 ToLower。主例的 A 和末尾的 a 变成同一个 rune,第一对就能相等。

3i<j 时比 t[i] 与 t[j]。中间那个字符在奇数长度下不会进入比较。

4洗完为空时 j 起手是 −1,循环不进,返回 true,符合空串是回文。

总结

只比小写字母数字,两头对碰。主例洗成 amanaplanacanalpanama,对到中间都相等。

  • 空格和标点不是失败,是「这一侧再走一格」。先清洗和原地跳过是同一条规则。
  • 大小写必须先统一,否则主例第一对 A 与 a 会错成 false。
  • "race a car" 洗完在 c 对 a 处失败,不是因为空格。
同族题目
LC5最长回文子串LC680验证回文串 IILC151反转字符串中的单词