題目描述:設計 MagicDictionary,支援兩種操作:buildDict(words) 以字串陣列建立字典,search(searchWord) 判斷是否能將 searchWord 中恰好一個字元替換成另一個字元後,使結果與字典中某個字完全相符(不可增刪字元,只能替換一個)。
解題思路:使用 Trie 儲存字典,搜尋時以 DFS 模擬「允許一次失配」。在 DFS 函式中傳入目前節點、searchWord 的當前位置 i,以及允許剩餘失配次數 diff(初始為 1)。
i == searchWord.size():檢查目前節點是否為終止節點 且 diff == 0(已恰好使用一次失配)。c = searchWord[i],枚舉 26 個字母 j:若 j != c - 'a',將 diff 扣 1;若子節點存在且 diff >= 0,遞迴繼續搜尋。true 即可提早回傳 true。此方法確保:完全相同的路徑(diff=1 消耗了 0 次,未回 0)不會誤判,只有恰好替換一個字元的情況才成功。
時間複雜度:buildDict O(N × L),N 為字典大小,L 為平均字串長度。search 最壞 O(26 × L)(每層最多嘗試 26 個子節點,深度為 L),實際因剪枝(diff < 0 立即停止)遠優於此上界。
空間複雜度:O(N × L × 26) — Trie 儲存所有字典字元所需節點,每節點含 26 個子指標。
buildDict(dictionary):
1. For each word in dictionary:
a. curr = root
b. For each char c in word:
- if child c missing, create new node
- advance curr to child c
c. Mark curr.isEnd = true
search(searchWord):
1. Call DFS(root, searchWord, 0, diff=1)
DFS(node, word, i, diff):
1. If i == len(word): return node.isEnd AND diff == 0
2. c = word[i]
3. For j in 0..25:
a. If node.children[j] is null: skip
b. newDiff = diff - (1 if j != c-'a' else 0)
c. If newDiff >= 0 AND DFS(node.children[j], word, i+1, newDiff): return true
4. Return false雜湊表 + 逐一比較:將字典儲存在雜湊集合中,search 時對 searchWord 的每個位置 i 枚舉 26 個替換字元,生成候選字串後查 HashSet。時間複雜度 O(L × 26) per search,實作更簡單但需要 O(N × L) 的額外記憶體存所有字串。不如 Trie 結構化,難以擴展到更複雜的模糊匹配。
暴力比對:search 時對字典每個詞計算與 searchWord 的差異字元數,若恰好為 1 則成功。時間複雜度 O(N × L) per search,空間 O(N × L)。在字典很大時效能差,但實現極簡單。
Trie + 預計算失配路徑:對 Trie 中每個節點預計算「若此位置失配,後續能否走到終止節點」的布林陣列,可加速多次搜尋。空間換時間,適合搜尋請求非常頻繁的場景。