剑指 Offer II 018. 有效的回文

144 阅读2分钟

题目

给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。

本题中,将空字符串定义为有效的 回文串

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出: true
解释: "amanaplanacanalpanama" 是回文串

示例 2:

输入: s = "race a car"
输出: false
解释:"raceacar" 不是回文串

提示:

  • 1 <= s.length <= 2 * 105
  • 字符串 s 由 ASCII 字符组成

解析

遍历 + 判断

遍历字符串s的每一个字符,如果发现是数字或者字母, 统一变成小写添加到数组s2中保存, 把数组s2的反转后得到s3, 最后判断 s2.join('') === s3.jon('') 二者是否相同即可。

注意由于数组的reverse方法会改变原数组,所以使用slice方法进行一次浅拷贝

双指针

思路比较巧妙,字符串是回文,说明左边的部分的子串和右边的子串相等

因此:声明左右两个指针分别指向数组的头尾,然后左指针向前走,右指针向后走,指针在遇到非数字或者字母的字符跳过,直到左右指针相遇,如果在左右指针走的过程它们指向的字符不相等,说明不是回文字符串,反之,当指针相遇时,说明左右的字符串都相同,说明是回文字符串

为什么是终止条件是 left < right?, 考虑当字符串s的长度为1, 如a, 它也满足回文字符串的定义

参考代码

遍历 + 判断

/**
 * @param {string} s
 * @return {boolean}
 */
var isPalindrome = function(s) {
    const numberOrDigitRegex = /[0-9a-zA-Z]/;
    const s2 = [];
    for (let char of s) {
        if (numberOrDigitRegex.test(char)) {
            s2.push(char.toLocaleLowerCase()); 
        }
    }
    const s3 = s2.slice().reverse();
    return s2.join('') === s3.join('');
};

双指针

/**
 * @param {string} s
 * @return {boolean}
 */
var isPalindrome = function(s) {
    let left = 0, right = s.length - 1;
    while(left < right) {
        while (!/[a-zA-Z0-9]/.test(s[left]) && left < right) {
            left++;
        }
        while (!/[a-zA-Z0-9]/.test(s[right]) && left < right) {
            right--;
        }
        if (s[left].toLowerCase() != s[right].toLowerCase()) {
            return false;
        }
        left++;
        right--;
    }
    return true;
};

原题链接

剑指 Offer II 018. 有效的回文