Problem

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

Palindrome Definition

Examples

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.

Note

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.

Solution

From start and end loop though the string, i.e., char array. If it is not alpha or number, increase or decrease pointers. Compare the alpha and numeric characters. The solution below is pretty straightforward.

We have seen similar problem - Check if given string is palindrome or not.

Method 1 - Using 2 Pointers and removing non-alphanumeric chars

Some interviewers may not allow above.

Code

Java
public static boolean isValidPalindrome(String s) {

	s = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();

	for (int i = 0; i<s.length(); i++) {
		if (s.charAt(i) != s.charAt(s.length() - 1 - i)) {
			return false;
		}
	}

	return true;
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(1)

Method 2 - Using 2 pointers after lowercasing the string

Code

Java
public boolean isPalindrome2(String s) {
	if (s == null || s.isEmpty()) {
		return true;
	}
	s = s.toLowerCase();
	for (int i = 0, j = s.length() - 1; i < j; ) {
		char leftChar = s.charAt(i);
		char rightChar = s.charAt(j);
		if (!isAlphaNum(leftChar)) {
			i++;
			continue;
		}
		if (!isAlphaNum(rightChar)) {
			j--;
			continue;
		}
		if (leftChar != rightChar) {
			return false;
		}
		i++;
		j--;
	}
	return true;
}

public boolean isAlphaNum(char a) {
	if ((a >= 'a' && a<= 'z') || 
		(a >= 'A' && a<= 'Z') || 
		(a >= '0' && a<= '9')) {
		return true;
	} 
	return false;
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(1)

Method 2 - Using 2 pointers - No shortcuts

Code

Java
public boolean isPalindrome(String s) {
	if (s == null) {
		return true;
	}
	if (s.length()<2) {
		return true;
	}
	int n = s.length();
	int i = 0; // left pointer
	int j = n - 1; // right pointer

	while (i<j) {
		// note upper bound is r and not n
		// this improves time complexity a bit
		while (i < j && !isAlphaNum(s.charAt(i))) {
			i++;
		}
	
		while (j > i && !isAlphaNum(s.charAt(j))) {
			j--;
		}

		if (i >= j)
			break;

		if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) {
			return false;
		}

		i++;
		j--;
	}
	return true;
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(1)

Method 4 - Using Stack

This solution removes the special characters first.

Code

Java
public boolean isPalindrome(String s) {
	// a shortcut
	s = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();

	int len = s.length();

	if (len < 2) {
		return true;
	}

	Stack<Character> stack = new Stack<Character>();

	int index = 0;

	while (index < len / 2) {
		stack.push(s.charAt(index));
		index++;
	}

	if (len % 2 == 1) {
		index++;
	}

	while (index < len) {
		if (stack.empty()) {
			return false;
		}
			
		char temp = stack.pop();

		if (s.charAt(index) != temp) {
			return false;
		} else {
			index++;
		}

	}

	return true;
}

Complexity

  • ⏰ Time complexity: O(n)
  • 🧺 Space complexity: O(n)