# Valid Palindrome

## Question

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

**Notice**

Have you consider that the string might be empty? This is a good question to ask during an interview.

For the purpose of this problem, we define empty string as valid palindrome.

**Example**

`"A man, a plan, a canal: Panama"` is a palindrome.

`"race a car"` is not a palindrome.

**Challenge**

O(n) time without extra memory.

**Tags**

String Two Pointers Facebook Zenefits Uber

**Related Problems**

Medium Palindrome Linked List\
Medium Longest Palindromic Substring

## Analysis

根据Palindrome本身对称性的特点，比较适合用two pointers的方法来解决，因为这里对valid palindrome的定义不考虑非字母和数字的字符，就需要在移动pointers时候注意条件判断，除了下标的边界，还有`isAlphanumeric()`来判定是否为字母和数字。isAlphanumeric()可以用内置函数`Character.isLetter()`和`Character.isDigit()`，也可以根据`char` ASCII码的定义写出。补充：还有一个专门内置函数：`Character.isLetterOrDigit()`，用`Character.toLowerCase()`统一大小写。

Test Cases需要考虑周到，比如：

`" "`\
`"a..."`\
`"...a"`\
`"a.a"`

## Solution

```java
public class Solution {
    /**
     * @param s A string
     * @return Whether the string is a valid palindrome
     */
    public boolean isPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        int i = 0;
        int j = s.length() - 1;

        while (i < j) {
            while (i < s.length() && !isAlphanumeric(s.charAt(i))) {
                i++;
            }
            if (i == s.length()) {
                return true;
            }
            while (j >= 0 && !isAlphanumeric(s.charAt(j))) {
                j--;
            }

            if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) {
                break;
            } else {
                i++;
                j--;
            }
        }
        return (j <= i);
    }

    public boolean isAlphanumeric(char ch) {
        // return Character.isLetter(ch) || Character.isDigit(ch);
        boolean result = (ch <= 'z' && ch >= 'a') ||
            (ch <= 'Z' && ch >= 'A') ||
            (ch <= '9' && ch >= '0');
        return result;

    }
}
```

Another implementation of isAlphanumeric()

```java
    // Use Character methods isLetter(), isDigit()
    private boolean isAlphanumeric (char c) {
        return Character.isLetter(c) || Character.isDigit(c);
    }
```

A verbose way - filter alphanumeric first, then start expanding from middle point of the new string

Time - O(n), Space O(n)

```java
class Solution {
    public boolean isPalindrome(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (Character.isLetter(ch)) {
                sb.append(Character.toLowerCase(ch));
            } else if (Character.isDigit(ch)) {
                sb.append(ch);
            }
        }

        String t = sb.toString();
        System.out.println(t);

        int i, j;

        if (t.length() % 2 == 0) {
            // even number
            i = (t.length() - 1) / 2;
            j = i + 1;

        } else {
            // odd number
            i = t.length() / 2;
            j = i;
        }
        while (i >= 0 && j < t.length()) {
            if (t.charAt(i) != t.charAt(j)) {
                return false;
            }
            i--;
            j++;
        }

        return true;
    }
}
```

**\***(**Preferred**) **A concise, one pass (4ms, 92.32%)**

善用`Character.isLetterOrDigit()`函数，跳过非alphanumeric的字符，然后在比较时，用`Character.toLowerCase()`统一大小写。

```java
class Solution {
    public boolean isPalindrome(String s) {
        int l = 0;
        int r = s.length() - 1;
        while (l < r) {
            char lChar = s.charAt(l);
            char rChar = s.charAt(r);
            if (!Character.isLetterOrDigit(lChar)) {
                l++;
            } else if (!Character.isLetterOrDigit(rChar)) {
                r--;
            } else {
                if (Character.toLowerCase(lChar) != Character.toLowerCase(rChar)) {
                    return false;
                }
                l++;
                r--;
            }
        }
        return true;

    }
}
```

## Reference

<http://www.jiuzhang.com/solutions/valid-palindrome/>
