Problem

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.

Given a string s, return true if it is a palindrome, or false otherwise.

Example 1: Input: s = “A man, a plan, a canal: Panama” Output: true Explanation: “amanaplanacanalpanama” is a palindrome.

Example 2: Input: s = “race a car” Output: false Explanation: “raceacar” is not a palindrome.

Example 3: Input: s = ” ” Output: true Explanation: s is an empty string "" after removing non-alphanumeric characters. Since an empty string reads the same forward and backward, it is a palindrome.

Constraints:

  • 1 <= s.length <= 2 * 105
  • s consists only of printable ASCII characters.

Solution

Two Pointer - O(n) time - O(1) space

class Solution {
public:
    bool isPalindrome(string s) {
        for (int front = 0, back = s.length()-1; front <= back; front++, back--) {
            while(front < s.length() -1 && !isalnum(s[front])) front++;
            while(back > 0 && !isalnum(s[back])) back--;
            if (front > back) return true;
            if(tolower(s[front]) != tolower(s[back])) return false;
        }
        return true;
    }
};

We can improve this significantly in terms of actual runtime and readability with a while loop

class Solution {
public:
    bool isPalindrome(string s) {
        int front = 0, back = s.length() - 1;
        while(front <= back) {
            if (!isalnum(s[front])) { front++; continue; }
            if (!isalnum(s[back])) { back--; continue; }
            if(tolower(s[front]) != tolower(s[back])) return false;
            front++;
            back--;
        }
        return true;
    }
};

Even more improved is proper ordering of conditionals to avoid Branch Prediction misses

class Solution {
public:
    bool isPalindrome(string s) {
        int front = 0, back = s.length() - 1;
        while(front <= back) {
            if(isalnum(s[front]) && isalnum(s[back]) && tolower(s[front]) != tolower(s[back])) return false;
            if (!isalnum(s[front])) { front++; continue; }
            if (!isalnum(s[back])) { back--; continue; }
            front++;
            back--;
        }
        return true;
    }
};