0125-Valid-Palindrome

Problem Description

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.

Solution

核心觀念:回文即「從兩端往中間,對應字元一路相等」。用相向雙指針 lr,遇到非英數字元就單側跳過,兩側都是英數字元時才 tolower 後比較;不等即 false。不用真的建出「過濾後的新字串」,所以 O(1) 額外空間。

"A man, a plan, a canal: Panama"
 l→                          ←r
跳過空白/標點,只在兩側都是字母數字時比較:
 a==a  m==m  a==a ...  中間相遇 → true

方法一:相向雙指針(原地)— O(n)/O(1)

// Time: O(n)
// Space: O(1)
bool isPalindrome(string s) {
  int l = 0;
  int r = s.size() - 1;
  while (l < r) {
    if (!isalnum(s[l])) {
      ++l;
    } else if (!isalnum(s[r])) {
      --r;
    } else {
      if (tolower(s[l]) != tolower(s[r])) {
        return false;
      }
      ++l;
      --r;
    }
  }
  return true;
}
Warning

isalnum / tolower 的引數若是負值(char 在某些平台有號、遇到非 ASCII 位元組會變負)屬未定義行為。嚴謹寫法是轉 unsigned charisalnum(static_cast<unsigned char>(s[l]))。本題保證 ASCII 可省,但養成習慣較安全。

Tip

if / else if / else 一次只動一根指針,避免「同時跳兩側」漏掉比較。內層兩側都推進 ++l; --r; 才是真正比對成功的一步。

方法二:過濾後比對反轉 — O(n)/O(n)

先把英數字元濾出、轉小寫成新字串,再判斷它與自己的反轉是否相等。程式碼直觀,代價是 O(n) 額外空間。

// Time: O(n)
// Space: O(n)
bool isPalindrome(string s) {
  string f;
  for (char c : s) {
    if (isalnum(static_cast<unsigned char>(c))) {
      f += tolower(static_cast<unsigned char>(c));
    }
  }
  return equal(f.begin(), f.end(), f.rbegin());
}