--- title: 【面試】簡易實做 AutoComplete - Python date: 2019-01-03 is_modified: false disqus: cynthiahackmd categories: - "面試刷題" tags: - "LeetCode" - "Trie" - "AutoComplete" --- {%hackmd @CynthiaChuang/Github-Page-Theme %} <br> 這大概是我有經驗來最神奇的面試經驗了(●▼●;) 面試時間一個小時,基本上就是**就一個人寫code,另外一個人一直看著寫code..XD**。想當然爾,面試結果想當然是被考官狠狠洗臉了 QAQ <p class="illustration"> <img src="https://i.imgur.com/vM4ReDR.png" alt="就一個人寫code,另外一個人一直看著寫code"> (圖片來源: <a href="https://office12.gr/events/practice-a-new-programming-language-hscc/">Heraklion Innovation Map</a>) </p> 大概會好一陣忘不了這次面是的考題 `AutoComplete` 吧 <!--more--> ## Ultimate Autocomplete 那天面試官一開始就直接跳過自我介紹,端主菜了上個道 Design and Implement Question 給我 - 實做一個 **Ultimate Autocomplete**。 :::info **Ultimate Autocomplete** 在最短的時間和空間內以良好的順序輸出預測的單詞列表,在理想情況下,列表的第一個單詞是正確的選擇。 ::: ## 概念陳述 <p class="illustration"> <img src="https://i.imgur.com/9xIpkh9.png" alt="trip"> trip(圖片來源: <a href="https://jangin.github.io/2020/04/19/Implement-Trie/">JanGin's BLOG</a>) </p> 在寫程式碼之前,面試官要求先用文字寫下演算法後再進行實作。 聽到這個題目,第一反應是使用 [<mark>字典樹(Trie)</mark>](https://zh.wikipedia.org/wiki/Trie) 實做,先用目前使用者的輸入做為 prefix,進行搜尋找出所有以該詞為字首的單詞,再將取出的結果進行排序。 <br> 實作分幾個階段 1. 將字典中的單字建成字典樹 1. 從字典取出每個單字變成 char stream 2. 將 char stream 由根節點往深度方向尋訪,依序建出子樹 - 根節點為空字元,不應該寫入任何字元 - 若該節點為單字結尾,該節點應加上標記,並寫入額外資訊,例如:詞頻、詞性...等 3. 重複步驟 1.1 與 1.2,直到字典中所有單字都建立完畢。 <br> 2. 尋訪字典樹,找出所有符合的清單 1. 將使用者的輸入做為 prefix,進行搜尋找出目前所在節點 2. 使用深度優先,尋訪該節點的所有子樹 3. 回傳尋訪結果 結果包含完整單字(即由根節點到被標記為單字結尾節點的路徑所組成的字串)、詞頻、詞性...等額外資訊。 <br> 3. 將結果進行排序後輸出 - 排序時,可再加入如歷史資訊等訊息,使預測結果更符合使用者的需求 - 回傳結果應去除額外資訊 ## 實作 ```python= from collections import defaultdict class Node: def __init__(self): ## 簡單起見,額外資訊僅記錄詞頻 self.childs = defaultdict(Node) self.is_word_end = False self.frequency = 0 class Trie: def __init__(self): self.root = Node() def insert(self, word, freq): word = word.lower() current = self.root for char in word: current = current.childs[char] current.is_word_end = True current.frequency = freq def complete(self, prefix): prefix = prefix.lower() current = self.root for char in prefix: current = current.childs.get(char) if current == None: return [] results = self.find(current,list(prefix),[]) results.sort(key=lambda x: x[1],reverse=True) return [w for w,f in results] def find(self, node, prefix, results): if node.is_word_end: results.append(("".join(prefix),node.frequency)) if len(node.childs) <= 0 : return results for char, next_node in node.childs.items(): prefix.append(char) results = self.find(next_node,prefix,results) prefix.pop(-1) return results trip = Trie() words = [('this',40), ('that',80), ('there',70), ('what',60), ('where',50), ('when',11)] for w,f in words: trip.insert(w,f) prefix = 'whe' suggest_words = trip.complete(prefix) print(suggest_words) ``` ## 感想 果然靜下心來寫,陳述的會比較有條理些... 為啥面試的時候可以卡成那樣 Orz 然後 LeetCode 要乖乖刷阿,這次時間太趕根本來不及準備,[LeetCode No. 208](https://leetcode.com/problems/implement-trie-prefix-tree/) 是建 Trie 的阿阿阿阿 <br><br> > **本文作者**: 辛西亞.Cynthia > **本文連結**: [辛西亞的技能樹](https://cynthiachuang.github.io/Auto-Complete) / [hackmd 版本](https://hackmd.io/@CynthiaChuang/Auto-Complete) > **版權聲明**: 部落格中所有文章,均採用 [姓名標示-非商業性-相同方式分享 4.0 國際](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en) (CC BY-NC-SA 4.0) 許可協議。轉載請標明作者、連結與出處!