Sunday 演算法是 Boyer-Moore 的改良版,屬於「壞字符啟發式」家族。核心差異:BM 參考「失配位置的字符」,Sunday 參考「模式串對齊區間後的下一個字符」決定位移量。平均效能優於 BM 3-5 倍,實作更簡潔。本文提供完整 Java 實作並對比朴素搜尋。
pattern 中每個字符 最右出現位置 的映射表 Maptext 當前位置 pos 開始逐字符比對c = text[pos + pattern.length()]c 在模式串中:位移至 c 在模式串 最右 位置對齊c 不在模式串:直接跳過整個模式串長度 + 1import java.util.*;
public class SundaySearch {
private final String text;
private final String pattern;
private int pos = 0;
private final List matches = new ArrayList<>();
private final Map lastOccurrence = new HashMap<>();
public SundaySearch(String text, String pattern) {
this.text = text;
this.pattern = pattern;
buildLastOccurrenceMap();
}
private void buildLastOccurrenceMap() {
for (int i = 0; i < pattern.length(); i++) {
lastOccurrence.put(pattern.charAt(i), i); // 保留最右位置
}
}
/** 從指定位置嘗試匹配 */
private boolean matchAt(int pos) {
if (pos + pattern.length() > text.length()) return false;
for (int i = 0; i < pattern.length(); i++) {
if (text.charAt(pos + i) != pattern.charAt(i)) return false;
}
return true;
}
/** 朴素搜尋:每次位移 1 (基準比較) */
public List naiveSearch() {
pos = 0; matches.clear();
while (pos + pattern.length() <= text.length()) {
if (matchAt(pos)) matches.add(pos);
pos++;
}
return matches;
}
/** Sunday 搜尋 */
public List sundaySearch() {
pos = 0; matches.clear();
while (pos + pattern.length() <= text.length()) {
if (matchAt(pos)) {
matches.add(pos);
pos++; // 找到後移 1 繼續找下一個
} else {
int nextCharPos = pos + pattern.length();
if (nextCharPos >= text.length()) break; // 越界結束
char nextChar = text.charAt(nextCharPos);
Integer lastPos = lastOccurrence.get(nextChar);
if (lastPos == null) {
pos += pattern.length() + 1; // 字符不在模式串,大跳躍
} else {
pos += pattern.length() - lastPos; // 對齊最右出現位置
}
}
}
return matches;
}
// 測試主程式
public static void main(String[] args) {
String text = "hello 啊啊 阿道夫 adfsadfklf adf234masdfsdfdsfdsffwerwrewrerwerwersdf2666sdflsdfk";
String pattern = "adf";
SundaySearch searcher = new SundaySearch(text, pattern);
long t1 = System.nanoTime();
System.out.println("Naive: " + searcher.naiveSearch() + " time: " + (System.nanoTime() - t1) + "ns");
long t2 = System.nanoTime();
System.out.println("Sunday: " + searcher.sundaySearch() + " time: " + (System.nanoTime() - t2) + "ns");
}
}
| 元件 | 說明 |
|---|---|
lastOccurrence Map | 儲存模式串每字符最右索引,建表 O(m),查詢 O(1) |
matchAt() | 封裝逐字符比對邏輯,避免重複程式碼 |
| 位移計算 | pos += pattern.length() - lastPos 精準對齊最右匹配字符 |
| 邊界檢查 | nextCharPos >= text.length() 防止索引越界 |
m+1,時間 O(n/m)text="aaaaa...", pattern="aaab",退化為 O(n*m)| 演算法 | 預處理 | 最壞時間 | 平均時間 | 實作複雜度 |
|---|---|---|---|---|
| 朴素搜尋 | 無 | O(nm) | O(nm) | 極簡 |
| KMP | O(m) 建 next 表 | O(n+m) | O(n+m) | 中等 |
| Boyer-Moore | O(m+σ) 壞字符/好後綴 | O(nm) | O(n/m) 佳 | 複雜 |
| Sunday | O(m) 建最右位置表 | O(nm) | O(n) 優 | 簡單 |
String.indexOf() (JDK 9+ 使用 Two-Way 演算法) 已高度優化,一般情況優先使用內建方法Sunday 演算法以簡潔實作換取優異平均效能,是字串搜尋演算法教學與輕量級工具開發的優選。