题目描述
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回 false
。
示例 1:
输入: s = "A man, a plan, a canal: Panama" 输出:true 解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car" 输出:false 解释:"raceacar" 不是回文串。
示例 3:
输入:s = " " 输出:true 解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。 由于空字符串正着反着读都一样,所以是回文串。
提示:
1 <= s.length <= 2 * 105
s
仅由可打印的 ASCII 字符组成
题目分析
回文串:正着读反着读都一样,比如说:
0110
0shhs0
这个题目是将字母大小写看作一样,所以在做的时候可以将字母全部转换为大写或者全部转换为小写。
题目的判断的回文串中,不仅包括字母,还有数字。
所以在判断字符串中的字符是否是有效字符 (可以用来判断是否是回文的字符) 时 ,还要把数字考虑进去。可以使用 isLetterOrDigit 方法,我刚开始做的时候,就是没看到数字也算有效字符,就导致总是错误。所以,审题要仔细。
题目解析
解法一
先将字符串遍历一遍,剔除所有无效字符 (非字母和数字) ,将所有有效字符放在一个新字符数组里,再基于新的字符数组进行判断。
解法二
在不考虑字符串中有无效字符的情况下,比如:
ABCCBA
在这种情况下判断回文, 就可以设两个指针 left,right,分别指向字符串的最左边字符,和最右边字符。当判断到 left 和 right 指向的字符相等时,将 left 右移,right 左移,继续判断两个字符是否相等。直到遍历完整个字符串,即 left >= right。在此过程中,如果有两个元素不相等的情况,直接返回 false,说明该字符串不是回文串。反之,如果遍历完整个字符串,都没有,那就在循环结束后,返回 true 即可。
对于含有无效字符的字符串的判断,就只用基于不含无效字符的解法上加一步,那就是判断是否是无效字符,如果是无效字符,那直接跳过即可。直到 left 和 right 指向的字符都是有效字符时,再判断 left 和 right 指向字符是否相等,不等,返回 false;相等,继续向后判断。
在跳过无效字符时,也需要实时判断 left < right,不能跳过了。
在跳过无效字符过后,对两个字符进行相等判断之前,也需要判断 left < right,因为在跳跃之后,可能会出现 left == right 的情况,那此时指向的是同一个字符,无需判断。算是一个小优化,其实不判断也不会错,只是效率没那么高。
时间复杂度:O(N)
空间复杂度:O(1)
代码
class Solution {
public boolean isPalindrome(String s) {
int left = 0, 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(left < right){
//判断两个字符是否相等
if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
return false;
}
left++;
right--;
}
}
return true;
}
}