迴文字串是指在不分大小寫的情況下比較字母時,從頭到尾與從尾到頭讀起來都相同的字串,而迴文檢查工具透過套用一條明確的正規化政策,能在數秒內判斷這個問題。該工具接受一個單字、片語、句子或數字,先進行 Unicode NFKC 正規化,再以固定的英文語系轉為小寫,僅保留 Unicode 字母與數字,然後從外側向中心逐一比較字碼點。由於比較用的字串會與判定結果以及保留字元數一併完整顯示,你可以將其複製回 Java 測試案例中,在工具之外重現相同的答案。整個處理流程都在瀏覽器中執行,不會上傳任何內容,且僅使用一條公開規則——不進行特定語言的轉寫、去腔調或發音比較。這種可預期的合約正是讓迴文檢查工具成為驗證你在 Java 中剛寫好的迴文邏輯(無論你使用的是 StringBuilder.reverse()、雙指標掃描或遞迴)的可靠仲裁器。

how to check palindrome string in java
Java 迴文字串檢查:三種可行方法

迴文字串的定義方式

根據《韋氏字典》,迴文從頭讀或從尾讀都相同,字典收錄的範例從像 dad 與 1881 這類單字,到著名的片語 A man, a plan, a canal: Panama。問題在於,人類對於大小寫、空格與標點的慣例因比賽而異。以逐字元比較來說,Racecar 與 racecar 會被視為不同;採用去標點規則則會讓 race car 合格;採用去腔調規則則會讓 Été 與嚴格的逐字母掃描有不同的處理結果。迴文檢查工具透過清楚陳述其規則來解決這個問題:忽略大寫字母、空格、標點、符號與表情符號,保留 Unicode 字母與數字,套用 NFKC,轉為小寫,然後從兩端向內比較字碼點。這讓該規則明確、可重現,並易於在 JUnit 測試中對應。當你撰寫 Java 程式碼時,第一個決策就是你要採用哪種規則——精確比對、忽略大小寫,或忽略非字母數字字元——因為每種規則對同一輸入會產生不同的判定結果。

三種 Java 迴文字串檢查方法

最常見的 Java 方法是 StringBuilder.reverse()、雙指標掃描與遞迴。每種都值得了解,因為面試偏好不同的風格,且各自有不同的邊界情況行為。請選擇與題目實際描述的規則相符的方法。

StringBuilder.reverse() — 最短的精確比對方法

public static boolean isPalindromeReverse(String s) {   String reversed = new StringBuilder(s).reverse().toString();   return s.equals(reversed); }

這是精確比對:不處理空格、標點與大小寫,因此 "racecar" 會通過而 "Racecar" 不會。當題目說「精確比對字串」時,這是正確的方法。它會配置一個新字串,因此若是熱路徑,你會偏好下方介紹的雙指標掃描。

雙指標掃描 — 忽略大小寫,忽略非字母數字字元

public static boolean isPalindromeIgnoreCase(String s) {   int left = 0, right = s.length() - 1;   while (left < right) {     char l = s.charAt(left);     char r = s.charAt(right);     if (!Character.isLetterOrDigit(l)) { left++; continue; }     if (!Character.isLetterOrDigit(r)) { right--; continue; }     if (Character.toLowerCase(l) != Character.toLowerCase(r)) return false;     left++; right--;   }   return true; }

這對應迴文檢查工具對 ASCII 子集所做的處理:跳過任何非字母或數字的字元,並在比較前將其餘字元轉為小寫。對於 Unicode 腔調字元與輔助字元,Java 的 charAt 是以 UTF-16 字碼單位運作,可能會切開表情符號或代理配對。如需討論如何在執行此掃描前從 Java 字串去除標點,請參考從字串去除字串中的標點(Java)指南。

遞迴 — 面試的最愛

public static boolean isPalindromeRec(String s, int left, int right) {   if (left >= right) return true;   if (s.charAt(left) != s.charAt(right)) return false;   return isPalindromeRec(s, left + 1, right - 1); }

遞迴很優雅,但與 StringBuilder.reverse() 一樣具有精確比對的限制,除非你加入雙指標版本中的 isLetterOrDigit 與 toLowerCase 防護。遞迴在長字串上也有 StackOverflowError 的風險,因此對於超過數千字元的輸入,建議改用迭代掃描。

如何使用工具驗證 Java 的迴文輸出

一旦你的 Java 方法回傳 true 或 false,你會想依據一條公開規則來確認結果——特別是片語、帶腔調的輸入或數字字串。迴文檢查工具無需任何設定就能給你第二意見。

  1. 開啟迴文檢查工具,貼上你的 Java 方法原本收到的輸入,包含其原始的大小寫、空格與標點。
  2. 執行檢查,並從預覽面板複製正規化後的比較字串,以及其旁邊顯示的保留字元數。
  3. 讀取判定結果(是或否),並直接與 Java 方法的回傳值進行比較。
  4. 以相同的輸入但不同的規則再執行一次檢查——精確比對與忽略大小寫及標點——以了解你的 Java 程式碼實際實作的是哪一種。

如果你的 Java 雙指標掃描對同一輸入回傳 true,且迴文檢查工具也判定為是,代表兩者的正規化一致。如果結果不一致,差異幾乎都是來自大小寫處理、腔調保留,或非 ASCII 字母的比較是否得當。工具所公開的正規化字串讓找出這個差異變得輕而易舉。

Java 忽略而工具能處理的邊界情況

Java 的 StringBuilder.reverse() 與基於 charAt 的掃描都是以 UTF-16 字碼單位運作,而非 Unicode 字碼點。這代表包含像 U+10000 這類輔助字元的輸入,可能會被切成兩個代理半字元,產生錯誤結果。迴文檢查工具以 Unicode 字碼點逐一迭代,因此輔助字元絕不會被切開。該工具也會保留帶腔調的字母:Été 會正規化為 été,並在字碼點比對下回傳是。表情符號會被明確排除:像 "🙂🙂" 這類輸入會回傳錯誤,因為保留字串規則要求至少要有一個字母或數字。空輸入、格式錯誤的 UTF-16,以及超過一百萬個 Unicode 字碼點的輸入,會在沒有部分判定的情況下失敗,因此你不會因為工具悄悄截斷文字而得到誤導的「是」。Java 並不會為你強制執行這些限制,所以如果你想在正式環境中獲得相同行為,請在方法外包一層防護來拒絕空字串、驗證 UTF-16,並限制長度。

Java 方法與迴文檢查工具的比較

面向Java StringBuilder.reverse()Java 雙指標掃描迴文檢查工具
規則類型精確比對,大小寫敏感僅字母/數字,ASCII 不分大小寫僅字母/數字,NFKC,英文小寫
腔調處理無正規化無正規化保留,套用 NFKC
輔助字元可能切開代理配對可能切開代理配對以字碼點逐一
表情符號處理當作文字比較由 isLetterOrDigit 過濾過濾,若僅有表情符號則回傳錯誤
可見的比較字串無無有,完整正規化預覽
需要網路否否否,於瀏覽器中執行
輸入上限無內建無內建1,000,000 個字碼點

此表格摘要了各方法實際比較的內容。Java 方法讓你保有控制權,但需要你在程式碼中陳述規則;迴文檢查工具則提供一條固定、公開的規則,加上它實際比較的可見字串,這是除錯一個回傳結果不如預期的 Java 方法最簡單的方式。

在信任任一答案前須記住的限制

迴文檢查工具絕不會為了產生結果而截斷來源文字,也不會進行特定語言的轉寫,因此在其他規則下比較結果不同的詞彙,在此處依然會有不同的比較結果。Java 的 Character.toLowerCase 使用預設語系,而迴文檢查工具使用固定的英文語系;這在土耳其語帶點 i 的輸入上會造成差異,因為 Java 的預設語系與工具的英文語系結論不同。Java 的 StringBuilder.reverse() 與基於 charAt 的掃描都是使用 UTF-16 字碼單位,因此對於位於基本多語言平面之外的輸入,你應該先用 codePointAt 將字串正規化為字碼點,或改用基於字碼點的迴圈。這些方法都不會比較發音、識別詞界,也不會判斷句子是否有意義;迴文檢查工具不是 DNA 迴文分析器、抄襲偵測器或語意相似度系統。對於謎題驗證、課堂範例、識別字檢查與數字序列,只要事先明確陳述規則並加以對應,Java 方法與迴文檢查工具都會給出相同的答案。