USACO 經典題目 calfflac:讀取文本檔,忽略非字母、不分大小寫,找出最長回文子串,輸出長度與原文對應片段。本文提供 O(N²) 中心展開法 Java 實作,含完整原始碼與關鍵技巧解析。
letters (StringBuilder),並記錄每個字母在原文的索引 indexes[]maxLength 紀錄目前最長,maxIndex 紀錄中心索引indexes[] 映射回原文區間輸出import java.util.*;
import java.io.*;
class calfflac {
public static void main(String[] args) throws IOException {
BufferedReader f = new BufferedReader(new FileReader("calfflac.in"));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("calfflac.out")));
StringBuilder origin = new StringBuilder(20000);
StringBuilder letters = new StringBuilder(20000);
int[] indexes = new int[20000];
String temp;
while ((temp = f.readLine()) != null) {
origin.append(temp).append('\n');
}
int len = origin.length();
for (int i = 0; i < len; i++) {
char c = origin.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
letters.append(c);
indexes[letters.length() - 1] = i;
}
}
int maxLength = 1, maxIndex = 0;
len = letters.length();
for (int i = 0; i < len; i++) {
int length = maxLength + 1; // 只找更長的,同長保留最早
boolean isChanged = false; // 切換奇/偶數長度
for (; i - (length - 1) / 2 >= 0 && i + length / 2 < len; ) {
if (isPal(letters, i - (length - 1) / 2, i + length / 2)) {
maxLength = length;
maxIndex = i;
length += 2;
} else if (!isChanged) {
isChanged = true;
length++; // 奇->偶 或 偶->奇
} else break;
}
}
int start = indexes[maxIndex - (maxLength - 1) / 2];
int end = indexes[maxIndex + maxLength / 2];
String result = origin.substring(start, end + 1);
out.println(maxLength);
out.println(result);
out.flush(); out.close(); System.exit(0);
}
// 判斷 s[i..j] 是否為回文 (不分大小寫)
static boolean isPal(StringBuilder s, int i, int j) {
for (; i < j; i++, j--) {
char c1 = s.charAt(i), c2 = s.charAt(j);
if (c1 != c2 && (c1 - c2) % 32 != 0) return false; // ASCII 大小寫差 32
}
return true;
}
}
| 技巧 | 說明 |
|---|---|
索引映射陣列 indexes[] | 只儲存字母在原文的位置,最終用 origin.substring(start, end+1) 完美還原含標點、換行的原始片段 |
大小寫忽略判斷 (c1-c2)%32!=0 | 利用 ASCII 碼大小寫差值固定為 32,避免呼叫 Character.toLowerCase() 開銷 |
| 同長度保留最早 | 只在 length = maxLength + 1 起跳,找到更長才更新,自然保證最早出現優先 |
| 奇/偶數長度交替 | isChanged 旗標控制:先試當前長度,失敗則切換奇偶 (+1) 再試,再失敗才結束該中心點 |
letters、indexes輸入 (calfflac.in):
"Hello, World! Madam, I'm Adam."
輸出 (calfflac.out):
11
"Madam, I'm Adam"