Skip to main content

125. Valid Palindrome

easyAsked at Bloomberg

Given a string, determine if it's a palindrome after removing non-alphanumeric characters and ignoring case. Bloomberg uses this to test two-pointer fluency plus a careful handling of mixed-content strings.

By Alex Chen, Founder, InterviewChamp.AI · Last verified

Source citations

Public interview reports confirming this problem appears in Bloomberg loops.

  • Glassdoor (2026-Q1)Bloomberg phone-screen reports cite Valid Palindrome as a recurring string + two-pointer warm-up.
  • Blind (2025-11)Bloomberg SWE new-grad reports list this among the most common Round 1 questions.

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.

Constraints

  • 1 <= s.length <= 2 * 10^5
  • s consists only of printable ASCII characters.

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: After removing non-alphanumeric chars, s = "" which is a palindrome.

Approaches

1. Two-pointer with in-place skip (optimal)

Two pointers from both ends. Skip non-alphanumeric on each side; compare lowercased characters.

Time
O(n)
Space
O(1)
function isPalindrome(s) {
  const isAlnum = (ch) => /[a-z0-9]/i.test(ch);
  let i = 0, j = s.length - 1;
  while (i < j) {
    while (i < j && !isAlnum(s[i])) i++;
    while (i < j && !isAlnum(s[j])) j--;
    if (s[i].toLowerCase() !== s[j].toLowerCase()) return false;
    i++; j--;
  }
  return true;
}

Tradeoff: Bloomberg's preferred answer. O(1) extra space — no string allocation. The inner while-loops handle skipping cleanly.

2. Filter then compare reverse

Build a cleaned lowercased string, compare to its reverse.

Time
O(n)
Space
O(n)
function isPalindromeFilter(s) {
  const cleaned = s.toLowerCase().replace(/[^a-z0-9]/g, '');
  return cleaned === cleaned.split('').reverse().join('');
}

Tradeoff: Concise but O(n) extra space. Mention as the 'simple version' then offer the two-pointer for the production answer.

Bloomberg-specific tips

Bloomberg interviewers grade specifically on the IN-PLACE two-pointer — they want O(1) extra space. State 'I'll use two pointers from both ends and skip non-alphanumeric characters' before coding.

Common mistakes

  • Forgetting that 'alphanumeric' includes digits, not just letters.
  • Off-by-one when skipping (i < j must be re-checked inside the inner while-loops).
  • Mishandling unicode or assuming only English letters — read the constraints (ASCII).

Follow-up questions

An interviewer at Bloomberg may pivot to one of these next:

  • Valid Palindrome II (LC 680) — allow deleting at most one character.
  • Longest Palindromic Substring (LC 5) — expand around center.
  • Palindrome Linked List (LC 234) — reverse second half then compare.

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

FAQ

Why not just filter then reverse?

The filter+reverse version allocates O(n) extra. Two-pointer is O(1) and Bloomberg's rubric explicitly grades for it. Both pass on correctness.

Should I use regex inside the loop?

On a per-char check, regex is fine but slower than a direct character-range check. Either is acceptable in interview; mention the tradeoff if asked.

Free learning resources

Curated free links for this problem.

Practice these live with InterviewChamp.AI

Drill Valid Palindrome and other Bloomberg interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →