1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public boolean isPalindrome(String s) { s = s.toLowerCase(); char[] c = s.toCharArray();
int left = 0; int right = c.length - 1; while (left < right) { while (left < right && !Character.isLetterOrDigit(c[left])) { left += 1; } while (left < right && !Character.isLetterOrDigit(c[right])) { right -= 1; } if (left != right && c[left] != c[right]) { return false; } else if (left == right) { break; } else { left += 1; right -= 1; } } return true; } }
|
至于下面这个写法, 更加保守, 不是上来就先找, 而是一次只移动left或者right. 这样看来逻辑上更好理解一些, 写也更容易写.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public boolean isPalindrome(String s) { s = s.toLowerCase(); char[] c = s.toCharArray();
for (int i = 0, j = c.length - 1; i < j;) { if (!Character.isLetterOrDigit(c[i])) i++; else if (!Character.isLetterOrDigit(c[j])) j--; else if (c[i++] != c[j--]) return false; } return true; } }
|
这道题想到了用双指针, 但是总感觉怪怪的. 当时想的是让left先找到一个字母或数字, 然后再让right找到一个字母或者数字, 然后看二者是否一样. 这里的问题是比如left在找到数字字母前就和right相遇了, 这代表着什么. 需要注意的是left和right指向的是string中未被验证部分的头和尾, 不会是right指向一个被验证完的char然后等left去找下一个字母或数字, 因为验证完后, left和rihgt都会向中间移动一位. 因此在left去寻找的时候, right指向的是未被验证的部分的尾, 在right寻找的时候, left指向的是未被验证部分的最靠前的数字或字母. 此时我们可以想想看, 如果left寻找的时候遇到了right, 那么不管right指向的是数字字母也好其他字符也好, 此时满足palindrome的要求. 当right遇到left的时候, left一定指向一个字母, 于是也满足. 因此第一种写法也是可以的.
换种想法, 不要想着left或者right直接跑到和对方重合会怎么样. 想一想如果返回false是什么条件?
此时是left和right都指向字母或者数字, 并且二者不等的时候. 因此只要在二者都是valid字符的条件下才可能出现false. 当二者都处于指向非valid字符的时候, 是不可能出现false的情况的.
时间复杂度: O(n)
空间复杂度: O(n) 因为创建了一个char array(当然也可以不创建)