Coding Interview Prep · درس

البحث عن البادئات وStarts-With

أضف دالة starts_with تُرجع true إذا شاركت أي كلمة مُدرجة في بادئة معينة، واستخدمها لتنفيذ اقتراحات الإكمال التلقائي.

الدرس 2 من 413 خطوة

البحث عن البادئات وStarts-With درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

قوة استعلامات البادئات

تتمثل الميزة الأساسية لـTrie مقارنةً بـhash map في كفاءة الاستعلام عن البادئات. يجيب استعلام البادئة عن أسئلة مثل: «كم عدد الكلمات المخزّنة التي تبدأ بهذه البادئة؟»، أو «ما جميع الكلمات المخزّنة التي تبدأ بهذه البادئة؟»، أو ببساطة «هل توجد أي كلمة تبدأ بهذه البادئة؟». تعمل هذه الاستعلامات بتعقيد O(p)، حيث p هو طول البادئة، بصرف النظر عن إجمالي عدد الكلمات المخزّنة، ما يجعل Tries مثالية للإكمال التلقائي واقتراحات البحث.

الدالة starts_with

تعيد starts_with(prefix) القيمة True إذا بدأت أي كلمة مخزّنة بالبادئة المعطاة. تتبّع Trie باتباع كل حرف من حروف البادئة. إذا أمكن اتباع جميع الحروف دون مواجهة حافة مفقودة، تكون البادئة موجودة، وتبدأ بها كلمة واحدة على الأقل. يشبه التنفيذ تنفيذ search تمامًا، باستثناء أننا نعيد True فور الانتهاء من التتبّع، ولا نتحقق من is_end.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()
    
    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True
    
    def starts_with(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node.children:
                return False
            node = node.children[c]
        return True

t = Trie()
for w in ['hello','help','world','word']:
    t.insert(w)
print(t.starts_with('hel'))   # True
print(t.starts_with('wor'))   # True
print(t.starts_with('xyz'))   # False

الإكمال التلقائي: العثور على جميع الكلمات ذات البادئة

لتنفيذ الإكمال التلقائي، تتبّع Trie حتى عقدة نهاية البادئة، ثم نفّذ DFS أو BFS من تلك العقدة لجمع جميع الكلمات المتفرعة منها. أضف البادئة إلى بداية كل لاحقة جُمعت لإعادة بناء الكلمات الكاملة. تعمل هذه العملية بتعقيد O(p + W)، حيث W هو العدد الإجمالي للحروف في جميع الكلمات المطابقة.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()
    
    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True
    
    def autocomplete(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node.children:
                return []
            node = node.children[c]
        # DFS from prefix end node
        results = []
        def dfs(n, path):
            if n.is_end:
                results.append(prefix + path)
            for char, child in n.children.items():
                dfs(child, path + char)
        dfs(node, '')
        return results

t = Trie()
for w in ['apple','app','application','apply','apt']:
    t.insert(w)
print(t.autocomplete('app'))  # ['app','apple','apply','application']

إرجاع الاقتراحات المرتبة

للحصول على إكمال تلقائي مرتب، تتبّع الأبناء بترتيب أبجدي أثناء DFS، وذلك بالتكرار على sorted(node.children.items()). وبما أن children مخزّنة في dict، يضيف ذلك تكلفة O(ALPHABET_SIZE × depth)، لكنه يضمن نتائج مرتبة معجميًا. أما Trie المعتمدة على مصفوفة فتتتبّع الأبناء دائمًا بترتيب أبجدي، لأن الفهارس من 0 إلى 25 مرتبة.

def dfs_sorted(node, prefix, results):
    if node.is_end:
        results.append(prefix)
    for char in sorted(node.children.keys()):  # alphabetical order
        dfs_sorted(node.children[char], prefix + char, results)

print('Iterating children in sorted order gives lex-sorted suggestions')

أفضل k من اقتراحات الإكمال التلقائي

للحصول على أفضل k من الاقتراحات حسب التكرار، أضف إلى كل عقدة قيمة count لعدد مرات البحث عن الكلمة التي تنتهي عندها. وعند جمع الاقتراحات، استخدم max-heap بحجم k. يقلل ذلك مجموعة نتائج DFS، التي حجمها O(W)، إلى O(k) دون إنشاء جميع التطابقات فعليًا. تجمع محركات البحث الواقعية بين تتبّع بادئات Trie وبيانات التكرار لتقديم اقتراحات سريعة وملائمة.

تنفيذ Trie للمسألة LeetCode 208

تطلب مسألة LeetCode 208 «Implement Trie (Prefix Tree)» تحديدًا: insert(word)، وsearch(word) التي تعيد قيمة منطقية للتطابق التام، وstartsWith(prefix) التي تعيد قيمة منطقية لمطابقة البادئة. وهذا هو تنفيذ Trie القياسي. تذكّر: يتطلب search ضبط is_end=True، بينما يتطلب startsWith وجود مسار البادئة فقط.

class Trie:
    def __init__(self):
        self.root = {}
    
    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node:
                node[c] = {}
            node = node[c]
        node['#'] = True  # '#' marks word end
    
    def search(self, word):
        node = self.root
        for c in word:
            if c not in node: return False
            node = node[c]
        return '#' in node
    
    def startsWith(self, prefix):
        node = self.root
        for c in prefix:
            if c not in node: return False
            node = node[c]
        return True

t = Trie()
t.insert('apple')
print(t.search('apple'))      # True
print(t.search('app'))        # False
print(t.startsWith('app'))   # True

استخدام '#' كعلامة نهاية (Trie باستخدام Dict)

يخزّن اختصار أنيق Trie على هيئة قواميس متداخلة، مع مفتاح حارس خاص مثل '#' لتمييز نهايات الكلمات، وبذلك يلغي الحاجة إلى فئة TrieNode. هذا الأسلوب موجز ومناسب للمقابلات، لكنه أقل وضوحًا قليلًا من كائنات TrieNode الصريحة. كلا التنفيذين مقبول، وإصدار القاموس أسرع في الكتابة عند ضيق الوقت.

أطول بادئة مشتركة باستخدام Trie

للعثور على أطول بادئة مشتركة لقائمة من السلاسل، أدرج جميع السلاسل في Trie، ثم تتبّع من الجذر المسار الوحيد الموجود ما دام: (1) للعقدة الحالية ابن واحد بالضبط، و(2) أن تكون is_end هي False. توقف عند اختلال أي من الشرطين. يمثّل المسار المتتبَّع أطول بادئة مشتركة.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

def longest_common_prefix(words):
    root = TrieNode()
    for word in words:
        node = root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True
    
    prefix = []
    node = root
    while len(node.children) == 1 and not node.is_end:
        char, node = next(iter(node.children.items()))
        prefix.append(char)
    return ''.join(prefix)

print(longest_common_prefix(['flower','flow','flight']))  # 'fl'
print(longest_common_prefix(['dog','racecar','car']))     # ''

مسألة Replace Words

Replace Words ‏(LeetCode 648): معطى قاموس من كلمات الجذر وجملة، استبدل كل كلمة في الجملة بأقصر جذر مطابق من القاموس. أدرج جميع الجذور في Trie. ولكل كلمة في الجملة، تتبّع Trie حتى العثور على نهاية جذر، ثم أعد ذلك الجذر ليكون البديل. وإذا لم يطابق أي جذر، فأبقِ الكلمة الأصلية. تعمل هذه الطريقة بتعقيد O(total chars) بدلًا من O(n × m) في الحل بالقوة الغاشمة.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

def replaceWords(dictionary, sentence):
    root = TrieNode()
    for word in dictionary:
        node = root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True
    
    def find_root(word):
        node = root
        for i, c in enumerate(word):
            if c not in node.children: break
            node = node.children[c]
            if node.is_end:
                return word[:i+1]
        return word
    
    return ' '.join(find_root(w) for w in sentence.split())

print(replaceWords(['cat','bat','rat'], 'the cattle was rattled by the battery'))

مسألة Map Sum Pairs

Map Sum ‏(LeetCode 677): أدرج أزواج المفتاح والقيمة، ثم أعد مجموع جميع القيم التي تحتوي مفاتيحها على بادئة معيّنة. أضف إلى كل TrieNode حقل val. عند الإدراج، تتبّع المسار حتى النهاية واضبط القيمة؛ وعند إجراء استعلامات المجموع، تتبّع المسار حتى عقدة نهاية البادئة، ثم احسب مجموع جميع حقول val الموجودة أسفلها باستخدام DFS. وبدلًا من ذلك، خزّن المجموع التراكمي في كل عقدة أثناء الإدراج لإجراء الاستعلامات بتعقيد O(p).

تنفيذ الإكمال التلقائي مع نتائج محدودة

في أنظمة autocomplete المستخدمة في بيئات الإنتاج، لا يُعد إرجاع جميع الكلمات التي تبدأ ببادئة معينة عمليًا عندما تطابقها آلاف الكلمات. بدلًا من ذلك، استخدم كومة عظمى بحجم k أثناء اجتياز DFS، واحتفظ بأعلى k كلمات من حيث النقاط التي عثرت عليها حتى الآن. أوقف فروع DFS مبكرًا إذا لم يكن بإمكانها احتواء كلمة ضمن أفضل k كلمات (بالتشذيب وفق الحد الأعلى للنقاط). يحقق هذا التعقيد O(p + k × log k) لكل استعلام يعيد k اقتراحات، وهو أفضل بكثير من جمع جميع التطابقات.

تحقق سريع

اختبر مدى فهمك لمفاهيم Data Structures & Algorithms — Coding Interview Prep التي تناولها هذا الدرس.

مراجعة الدرس

تعلمت في هذا الدرس أن: ‏starts_with يجتاز مسار البادئة ويعيد True إذا كانت موجودة — ولا حاجة إلى التحقق من is_end، وأن autocomplete يستخدم DFS لجمع جميع الكلمات من عقدة نهاية البادئة عبر إلحاق الأحرف أثناء التعمق، وأن إضافة أعداد أو قيم إلى العقد تتيح تنفيذ استعلامات المجموع واقتراحات أفضل k عناصر. في الخطوة التالية، سنضيف مطابقة أحرف البدل والتعبيرات النمطية إلى شجرة Trie.

البدء مجانًا

تعلم Coding Interview Prep مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
90
الدروس
360

الأسئلة الشائعة

هل درس «البحث عن البادئات وStarts-With» مجاني؟

نعم — نص درس «البحث عن البادئات وStarts-With» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «البحث عن البادئات وStarts-With»؟

أضف دالة starts_with تُرجع true إذا شاركت أي كلمة مُدرجة في بادئة معينة، واستخدمها لتنفيذ اقتراحات الإكمال التلقائي. تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «البحث عن البادئات وStarts-With»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الفئة TrieNode: الإدراج والبحث
  2. البحث عن البادئات وStarts-With
  3. البحث باستخدام الرموز البديلة والتعبيرات النمطية في Trie
  4. البحث عن الكلمات II: Trie والتراجع على شبكة
← العودة إلى Coding Interview Prep