""" TEST & GIẢI THÍCH: Module Trie ================================ File này test và giải thích chi tiết cách Trie hoạt động """ import sys sys.path.append('src') from search.trie import Trie, TrieNode def explain_trie(): """Giải thích chi tiết Trie""" print("=" * 80) print("GIẢI THÍCH: TRIE (PREFIX TREE)") print("=" * 80) print(""" TRIE LÀ GÌ? ----------- - Cấu trúc dữ liệu dạng CÂY - Mỗi NODE = 1 ký tự - Dùng để tìm PREFIX (từ bắt đầu bằng...) VÍ DỤ TRỰC QUAN: ---------------- Lưu 3 từ: "làm", "lau", "lên" (root) | l /|\\ / | \\ à a ê | | | m u n - Duyệt từ root → l → à → m = "làm" - Duyệt từ root → l → a → u = "lau" - Duyệt từ root → l → ê → n = "lên" TẠI SAO NHANH? -------------- - Tìm "la" chỉ cần: root → l → a → thu thập nhánh con - Độ phức tạp: O(m) với m = độ dài query - KHÔNG cần duyệt tất cả documents! """) input("\nNhấn Enter để xem demo...") def demo_step_by_step(): """Demo từng bước""" print("\n" + "=" * 80) print("DEMO TỪNG BƯỚC") print("=" * 80) # Tạo Trie trie = Trie() # Dữ liệu test documents = [ "làm sạch nhà vệ sinh", # ID: 0 "làm sạch văn phòng", # ID: 1 "làm wc", # ID: 2 "lau sàn nhà", # ID: 3 "lau kính cửa sổ", # ID: 4 "vệ sinh toilet", # ID: 5 ] print("\n📥 BƯỚC 1: THÊM DOCUMENTS VÀO TRIE") print("-" * 80) print("Mỗi document được lưu theo từng ký tự trong cây\n") for doc_id, doc in enumerate(documents): trie.insert(doc.lower(), doc_id) print(f"✓ Đã thêm [{doc_id}]: {doc}") # Giải thích cụ thể cho 2 documents đầu if doc_id == 0: print(" → Tạo đường: root → l → à → m → → s → ạ → c → h...") elif doc_id == 1: print(" → Tạo đường: root → l → à → m → → s → ạ → c → h... (chia sẻ prefix với doc 0)") elif doc_id == 2: print(" → Tạo đường: root → l → à → m → → w → c (chia sẻ 'làm ' với doc 0,1)") input("\nNhấn Enter để test tìm kiếm...") print("\n" + "=" * 80) print("🔍 BƯỚC 2: TEST TÌM KIẾM") print("=" * 80) # Test case 1 print("\n📌 TEST 1: Tìm 'làm'") print("-" * 80) query = "làm" results = trie.search_prefix(query) print(f"Query: '{query}'") print(f"Cách hoạt động:") print(f" 1. Duyệt: root → l → à → m") print(f" 2. Đến node 'm', thu thập TẤT CẢ documents từ đây xuống") print(f" 3. Tìm thấy {len(results)} documents:") for doc_id in results: print(f" [{doc_id}] {documents[doc_id]}") input("\nNhấn Enter tiếp...") # Test case 2 print("\n📌 TEST 2: Tìm 'làm s'") print("-" * 80) query = "làm s" results = trie.search_prefix(query) print(f"Query: '{query}'") print(f"Cách hoạt động:") print(f" 1. Duyệt: root → l → à → m → → s") print(f" 2. Đến node 's', thu thập documents") print(f" 3. KẾT QUẢ chính xác hơn (loại bỏ 'làm wc'):") print(f" 4. Tìm thấy {len(results)} documents:") for doc_id in results: print(f" [{doc_id}] {documents[doc_id]}") input("\nNhấn Enter tiếp...") # Test case 3 print("\n📌 TEST 3: Tìm 'lau'") print("-" * 80) query = "lau" results = trie.search_prefix(query) print(f"Query: '{query}'") print(f"Cách hoạt động:") print(f" 1. Duyệt: root → l → a → u") print(f" 2. Thu thập documents từ node 'u'") print(f" 3. Tìm thấy {len(results)} documents:") for doc_id in results: print(f" [{doc_id}] {documents[doc_id]}") input("\nNhấn Enter tiếp...") # Test case 4 - KHÔNG TÌM THẤY print("\n📌 TEST 4: Tìm 'wc' ❌") print("-" * 80) query = "wc" results = trie.search_prefix(query) print(f"Query: '{query}'") print(f"Cách hoạt động:") print(f" 1. Duyệt: root → w") print(f" 2. ❌ KHÔNG tìm thấy node 'w' ở root") print(f" 3. Return []") print(f"") print(f"❓ TẠI SAO KHÔNG TÌM THẤY?") print(f" - 'wc' NẰM Ở GIỮA câu: 'làm wc'") print(f" - Trie chỉ tìm được từ Ở ĐẦU câu") print(f" - Trong cây, 'w' nằm sau 'làm ' chứ không ở root") print(f"") print(f"💡 GIẢI PHÁP: Dùng INVERTED INDEX (module tiếp theo)") input("\nNhấn Enter để xem kết luận...") def conclusion(): """Kết luận""" print("\n" + "=" * 80) print("📝 KẾT LUẬN") print("=" * 80) print(""" ✅ TRIE TÌM ĐƯỢC: ----------------- ✓ "làm" → tất cả bắt đầu bằng "làm" ✓ "làm s" → tất cả bắt đầu bằng "làm s" (chính xác hơn) ✓ "lau" → tất cả bắt đầu bằng "lau" ✓ "vệ" → tất cả bắt đầu bằng "vệ" ❌ TRIE KHÔNG TÌM ĐƯỢC: ----------------------- ✗ "wc" → vì "wc" nằm Ở GIỮA câu "làm wc" ✗ "sạch" → vì "sạch" nằm sau "làm " (không phải prefix) ✗ "toilet" → vì "toilet" nằm sau "vệ sinh " 📊 ĐỘ PHỨC TẠP: --------------- - Thêm document: O(m) với m = độ dài text - Tìm kiếm: O(m + n) với m = độ dài query, n = số kết quả - Memory: O(tổng số ký tự của tất cả documents) 🎯 KHI NÀO DÙNG TRIE? --------------------- ✓ Autocomplete (gợi ý khi gõ) ✓ Prefix search (tìm từ bắt đầu bằng...) ✓ Spell checker ✓ IP routing ⏭️ MODULE TIẾP THEO: --------------------- → INVERTED INDEX → Tìm keyword ở BẤT KỲ ĐÂU trong text → Giải quyết vấn đề của Trie """) print("=" * 80) if __name__ == "__main__": explain_trie() demo_step_by_step() conclusion() print("\n✅ Hoàn thành! Bạn đã hiểu Trie chưa?") print("📂 Code thực tế: src/search/trie.py") print("⏭️ Tiếp theo: tests/test_2_inverted_index.py\n")