题目描述

✅ LCR 018. 验证回文串

image-20260928234906056

题意分析

判断一个字符串在忽略非字母数字字符、忽略字母大小写后,是否正着读和倒着读相同。数字需要保留,空格与标点等其他字符不参与比较;保留下来的字符仍按原先顺序排列。

题目输入为 ASCII 字符。过滤后没有任何字符,或只剩一个有效字符,都满足回文定义;并不要求原字符串本身没有标点,也不需要真正删除字符后返回新串,只需返回布尔结果。

解法:双指针跳过无效字符

核心思路

[!blue]

回文要求第一个有效字符与最后一个有效字符相同,第二个与倒数第二个相同,依次向中间配对。因此可以直接在原串两端寻找下一对有效字符,不必创建一份过滤后的字符串。

用 left 从左向右、right 从右向左扫描。每轮先分别跳过非字母数字字符;若两端仍分离,它们指向的就是过滤后序列中尚未比较的首尾字符。将双方按同一种规则转成小写后比较,不等便找到反例,立即返回 false。

比较通过后同时向内移动。指针外侧的有效字符已经按首尾顺序匹配,指针之间的部分仍待检查;忽略标点只改变寻找有效字符的位置,不改变它们在过滤序列中的先后关系。

跳过无效字符时也要检查 left < right。否则全部剩余字符都是标点时可能越界。若跳过后两指针相遇,剩余范围至多包含一个有效字符,不可能破坏回文;当前实现比较同一位置也必然通过。最终指针相遇或交错,所有需要的字符对都已验证,返回 true。

解题步骤

  1. 左右指针分别指向原串首尾,只在 left < right 时继续。
  2. 左指针跳过非字母数字字符,移动时持续检查左右边界。
  3. 右指针同样跳过无效字符,使用更新后的左边界限制移动。
  4. 将两端字符统一转为小写后比较,不相等立即返回 false。
  5. 两端同时内缩,继续检查;循环结束返回 true。

代码实现

class Solution {
    public boolean isPalindrome(String s) {
        int left = 0;
        int right = s.length() - 1;

        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                left++;
            }

            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                right--;
            }

            // 只比较字母数字字符,并忽略大小写。
            if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                return false;
            }

            left++;
            right--;
        }

        return true;
    }
}
func isPalindrome(s string) bool {
    left := 0
    right := len(s) - 1
    for left < right {
        for left < right && !isAlphaNum(s[left]) {
            left++
        }
        for left < right && !isAlphaNum(s[right]) {
            right--
        }

        // 有效字符统一转小写后比较。
        if toLower(s[left]) != toLower(s[right]) {
            return false
        }
        left++
        right--
    }
    return true
}

func isAlphaNum(ch byte) bool {
    return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
}

func toLower(ch byte) byte {
    if ch >= 'A' && ch <= 'Z' {
        return ch + 'a' - 'A'
    }
    return ch
}

复杂度分析

  • 时间复杂度:O(n)。两个指针只向中间移动,总移动次数为线性,每个位置只需常数次分类和比较。
  • 空间复杂度:O(1)。仅保存下标,不创建过滤串、反转串或额外数组。

关键点总结

[!green]

  • 忽略字符可以通过跳过下标实现,无需改写原字符串。
  • 首尾有效字符逐对比较,外侧已经确认,内侧逐渐缩小。
  • 字母与数字都有效,大小写归一化需同时作用于比较双方。
  • 空的有效序列和单个有效字符天然符合回文定义。

易错点总结

[!yellow]

  • 只跳空格、不跳其他符号:题目忽略所有非字母数字字符。
  • 把数字也过滤掉:数字属于需要参与对称比较的内容。
  • 仅转换一侧大小写:另一侧可能是大写或小写,双方应采用同一种规则。
  • 内层跳过时不检查相遇条件:纯标点区间可能让指针越界。
  • 比较相同后只移动一端:下一轮不再对应过滤序列的下一对首尾字符,会破坏配对关系。

相似题目

题目 难度 关联与区别
680. 验证回文串 II 简单 在双指针回文判断上增加一次删除机会,首次失配需要检查两种跳过方向。
234. 回文链表 简单 回文比较条件相同,但单链表不能直接反向访问,需要反转后半段或额外存储。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/58023379
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!