題目描述:給定兩個字串 haystack 和 needle,請回傳 needle 在 haystack 中第一次出現的起始索引。若 needle 不存在於 haystack 中,則回傳 -1。
解題思路:本題要求實作經典的字串搜尋功能(即 strStr())。最直覺的方法是滑動視窗暴力法:對 haystack 的每個位置 i,嘗試將從該位置開始長度為 m 的子字串與 needle 比對,若完全一致則回傳 i。更優的方法是 KMP 演算法:先對 needle 建立失敗函數(failure function / LPS 陣列),紀錄每個前綴的最長相同真前後綴長度,使搜尋時遭遇不匹配能直接跳過已知部分,避免重複比對,達到 O(n+m) 的線性時間。本題以 KMP 實作為主要解法。
時間複雜度:O(n + m) — 建立失敗函數需 O(m),KMP 主搜尋需 O(n),其中 n 為 haystack 長度、m 為 needle 長度。由於指標只往前移動,整體為線性時間。
空間複雜度:O(m) — 額外使用一個長度為 m 的 LPS 陣列儲存失敗函數,其餘為常數空間。
1. Handle edge case: if needle is empty, return 0.
2. Build LPS (failure function) array for needle:
a. Initialize lps[0] = 0, len = 0, i = 1.
b. While i < m:
- If needle[i] == needle[len]: lps[i] = len+1, advance both i and len.
- Else if len > 0: len = lps[len-1] (fall back without advancing i).
- Else: lps[i] = 0, advance i.
3. Search haystack using KMP:
a. Initialize i = 0 (haystack pointer), j = 0 (needle pointer).
b. While i < n:
- If haystack[i] == needle[j]: advance both i and j.
- If j == m: return i - j (match found).
- Else if mismatch and j > 0: set j = lps[j-1] (skip without moving i).
- Else: advance i.
4. Return -1 (needle not found).方法一:暴力滑動視窗 O(n*m)
對 haystack 的每個合法起始位置 i(0 到 n-m),逐一比對 haystack[i..i+m-1] 與 needle 是否相同。若完全匹配則回傳 i,否則繼續下一個位置。時間複雜度 O(n*m),空間複雜度 O(1)。實作簡單,適合面試快速寫出初始解。
方法二:KMP 演算法 O(n+m)(本題主要解法) 先建立 needle 的失敗函數陣列(LPS),搜尋過程中遭遇不匹配時利用 LPS 跳過已知相符的前綴,每個字元最多被比對兩次,達到線性時間。時間複雜度 O(n+m),空間複雜度 O(m)。
方法三:Rabin-Karp 滾動雜湊 O(n+m) 平均 對 needle 計算雜湊值,再對 haystack 每個長度 m 的子字串用滾動雜湊維護雜湊值(O(1) 更新),只有雜湊相同時才逐字比對以確認真正匹配。平均 O(n+m),最差 O(n*m)(雜湊衝突時)。
needle 需要在同一個 haystack 中多次搜尋(不同 needle),KMP 與 Rabin-Karp 各有何優劣?應如何選擇?needle 在 haystack 中「所有」出現的位置而非只有第一個,KMP 演算法應如何修改?len = lps[len-1] 而不是直接令 len = 0?請舉例說明若直接歸零會導致什麼錯誤結果。