Voorbereiding op programmeerinterviews · Les

Word Search II: trie + backtracking op een grid

Voeg alle doelwoorden in een trie in en voer DFS-backtracking uit op een 2D-bord om alle geldige woorden tegelijk te vinden in O(m × n × 4^L).

Les 4 van 413 stappen

Word Search II: trie + backtracking op een grid is een gratis Voorbereiding op programmeerinterviews-les op CoddyKit. Dit is les 4 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Voorbereiding op programmeerinterviews. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Voorbereiding op programmeerinterviews bevat in totaal 4 lessen.

Het probleem van Word Search II

Word Search II (LeetCode 212): gegeven een m × n-bord met tekens en een woordenlijst, vind je alle woorden die kunnen worden gevormd door opeenvolgende aangrenzende cellen (horizontaal of verticaal), waarbij elke cel slechts één keer mag worden gebruikt. Dit is moeilijker dan Word Search I (één woord), omdat we alle overeenkomende woorden tegelijk moeten vinden — Word Search I naïef uitvoeren voor elk woord kost O(W × m × n × 4^L), wat te traag is.

Waarom trie + backtracking?

Door alle doelwoorden in een trie in te voegen en vervolgens DFS-backtracking op het bord uit te voeren, kunnen we alle woorden tegelijk zoeken. Bij elke bordcel controleren we niet: 'vormt dit pad mijn doelwoord?', maar: 'komt dit pad overeen met een prefix in de trie?'. Zodra een trie-prefix niet meer overeenkomt, snoeien we de volledige DFS-tak — zo vermijden we dubbel werk voor alle woorden die dat prefix delen.

Een trie opbouwen uit een woordenlijst

Voeg alle woorden in een trie in. Sla het volledige woord op in de bladknoop (in node.word) in plaats van alleen een booleaanse waarde, zodat je het woord bij een volledige overeenkomst tijdens het backtracken direct aan de resultaten kunt toevoegen zonder het teken voor teken opnieuw op te bouwen.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.word = None  # stores the complete word if this is an end node

def build_trie(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.word = word  # mark complete word here
    return root

root = build_trie(['eat','oath','ot'])
print('Trie built with', len(root.children), 'root children')

DFS-backtracking op het raster

Start DFS vanaf elke cel op het bord. Doe bij elke stap het volgende: (1) controleer of het teken van de huidige cel als kind bestaat in de huidige trieknoop; (2) zo ja, markeer de cel als bezocht (zet deze op een sentinelwaarde zoals '#') en roep de functie aan voor de 4 buren; (3) herstel de cel na de recursieve aanroep (maak de markering ongedaan). Voeg het woord aan de resultaten toe wanneer een trieknoop een niet-None word heeft en zet het vervolgens op None om duplicaten te voorkomen.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.word = None

def findWords(board, 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.word = word
    
    m, n = len(board), len(board[0])
    result = []
    
    def dfs(i, j, node):
        c = board[i][j]
        if c not in node.children:
            return
        next_node = node.children[c]
        if next_node.word:
            result.append(next_node.word)
            next_node.word = None  # avoid duplicates
        board[i][j] = '#'  # mark visited
        for di, dj in [(-1,0),(1,0),(0,-1),(0,1)]:
            ni, nj = i+di, j+dj
            if 0<=ni<m and 0<=nj<n and board[ni][nj] != '#':
                dfs(ni, nj, next_node)
        board[i][j] = c  # restore
    
    for i in range(m):
        for j in range(n):
            dfs(i, j, root)
    
    return result

board = [['o','a','a','n'],['e','t','a','e'],['i','h','k','r'],['i','f','l','v']]
words = ['oath','pea','eat','rain']
print(findWords(board, words))  # ['oath','eat']

Complexiteitsanalyse

Tijd: O(m × n × 4^L), waarbij L de maximale woordlengte is. Voor elk van de m×n startcellen verkent DFS maximaal 4^L paden. De trie snoeit paden die met geen enkel woordprefix overeenkomen, waardoor het in de praktijk veel sneller is. De trie opbouwen kost O(W × L), waarbij W het aantal woorden is. Ruimte: O(W × L) voor de trie plus O(L) voor de diepte van de recursiestack.

Snoeien: bladknopen verwijderen na het vinden

Verwijder na het vinden van een woord de bladknoop uit de trie (zet niet alleen het woord op null) als de knoop geen kinderen heeft. Zo voorkom je dat volgende DFS-aanroepen dode takken opnieuw bezoeken. Wanneer de kinderen van een knoop na het vinden van het woord leeg zijn geworden, verwijder je de knoop uit de kinddictionary van de ouder. Deze optimalisatie is belangrijk wanneer veel woorden lange prefixen delen.

def dfs_with_pruning(i, j, node, board, m, n, result):
    c = board[i][j]
    if c not in node.children:
        return
    next_node = node.children[c]
    if next_node.word:
        result.append(next_node.word)
        next_node.word = None
    board[i][j] = '#'
    for di, dj in [(-1,0),(1,0),(0,-1),(0,1)]:
        ni, nj = i+di, j+dj
        if 0<=ni<m and 0<=nj<n and board[ni][nj] != '#':
            dfs_with_pruning(ni, nj, next_node, board, m, n, result)
    board[i][j] = c
    # Prune: if the node has no more children and no word, remove it
    if not next_node.children and not next_node.word:
        del node.children[c]

print('Leaf pruning removes exhausted trie branches during search')

Waarom word opslaan in Node beter is

Het volledige woord opslaan in de bladknoop van de trie (in plaats van het opnieuw opbouwen vanuit het DFS-pad) heeft twee voordelen: (1) ophalen van het woord in O(1) wanneer een overeenkomst wordt gevonden, in plaats van padreconstructie in O(L); (2) node.word = None instellen nadat het woord is gevonden zorgt voor een nette deduplicatie in O(1), zonder een aparte verzameling resultaten. Vooral voor Word Search II is het voorkomen van duplicaten belangrijk, omdat hetzelfde woord theoretisch via verschillende paden kan worden gevonden.

Bezochte cellen direct markeren

In plaats van een aparte visited-verzameling (die O(m × n) ruimte per DFS-pad zou vereisen), markeren we cellen direct door hun teken te vervangen door een sentinelwaarde zoals '#'. Nadat DFS terugkeert, herstellen we het oorspronkelijke teken. Deze techniek: (1) gebruikt O(1) extra ruimte per cel; (2) voorkomt automatisch dat een cel binnen één pad opnieuw wordt bezocht; (3) is volledig transparant voor de trie-doorloop, omdat '#' nooit in de trie voorkomt.

Te behandelen randgevallen

Belangrijke randgevallen: (1) dubbele woorden in de woordenlijst — sla ze op in een verzameling, of gebruik de truc node.word = None om duplicaten in de resultaten te voorkomen; (2) zeer lange woorden die de bordafmetingen overschrijden — ze kunnen niet worden gevormd, maar DFS handelt dit vanzelf af doordat er geen aangrenzende cellen meer zijn; (3) een bord met één cel — alleen woorden van één teken kunnen worden gevonden; (4) hetzelfde woord dat via verschillende paden kan worden gevonden — de truc node.word = None voorkomt dubbel tellen.

Vergelijking met de naïeve aanpak

Naïeve aanpak: voer voor elk van de W woorden Word Search I uit: O(W × m × n × 4^L). Met de trie worden alle woorden tegelijk gezocht: O(m × n × 4^L), ongeacht W. Voor W=1000 woorden van lengte 10 op een bord van 10×10 is de naïeve aanpak 1000× langzamer dan de trie-aanpak. De trie fungeert als een gedeeld prefixfilter dat de kosten over alle woorden verdeelt — een klassiek voorbeeld van het gebruik van een gegevensstructuur om een asymptotische verbetering te bereiken.

Samenvatting van de volledige oplossing

Volledige oplossing voor Word Search II: bouw een trie met de woorden en sla de woordtekenreeks op in de bladknoop. Voer voor elke bordcel DFS uit: controleer of het huidige teken in de huidige trieknoop voorkomt, markeer de cel als '#', roep de functie aan voor de 4 buren en herstel de cel. Voeg het woord aan de resultaten toe wanneer node.word niet null is en zet het vervolgens op null. Snoei optioneel lege trietakken na gebruik. Geef de resultatenlijst terug. Tijd: O(m×n×4^L), ruimte: O(W×L) voor de trie + O(L) voor de recursie.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.word = None

def findWords_final(board, words):
    root = TrieNode()
    for word in words:
        node = root
        for c in word:
            node = node.children.setdefault(c, TrieNode())
        node.word = word
    
    m, n = len(board), len(board[0])
    result = []
    
    def dfs(i, j, node):
        c = board[i][j]
        child = node.children.get(c)
        if not child:
            return
        if child.word:
            result.append(child.word)
            child.word = None
        board[i][j] = '#'
        for di, dj in [(-1,0),(1,0),(0,-1),(0,1)]:
            ni, nj = i+di, j+dj
            if 0<=ni<m and 0<=nj<n and board[ni][nj] != '#':
                dfs(ni, nj, child)
        board[i][j] = c
        if not child.children:
            del node.children[c]
    
    for i in range(m):
        for j in range(n):
            dfs(i, j, root)
    return result

Korte controle

Controleer je begrip van de concepten Data Structures & Algorithms — Coding Interview Prep uit deze les.

Samenvatting van de les

In deze les heb je geleerd dat Word Search II een trie gebruikt om tegelijk naar meerdere woorden te zoeken met gedeeld snoeien op prefixen, dat het opslaan van de woordtekenreeks in de bladknoop van de trie ophalen in O(1) en eenvoudige deduplicatie mogelijk maakt door de waarde na het vinden op None te zetten, en dat het direct markeren van bezochte cellen met '#' O(m×n) extra ruimte per DFS-pad voorkomt. Hiermee rond je de cursus Tries and String Algorithms af — je hebt een van de krachtigste tekenreeks-specifieke gegevensstructuren voor technische sollicitatiegesprekken onder de knie.

Gratis beginnen

Leer Voorbereiding op programmeerinterviews met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
90
Lessen
360

Veelgestelde vragen

Is de les “Word Search II: trie + backtracking op een grid” gratis?

Ja — de volledige tekst van “Word Search II: trie + backtracking op een grid” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Voorbereiding op programmeerinterviews wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Voorbereiding op programmeerinterviews bevat in totaal 4 lessen.

Wat leer ik in “Word Search II: trie + backtracking op een grid”?

Voeg alle doelwoorden in een trie in en voer DFS-backtracking uit op een 2D-bord om alle geldige woorden tegelijk te vinden in O(m × n × 4^L). Je oefent met Voorbereiding op programmeerinterviews door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Voorbereiding op programmeerinterviews te beginnen?

Ervaring vooraf is niet nodig. Voorbereiding op programmeerinterviews op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 4 van 4.

Hoe lang duurt de les “Word Search II: trie + backtracking op een grid”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Voorbereiding op programmeerinterviews?

Ja. Elke les over Voorbereiding op programmeerinterviews bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. TrieNode-klasse: invoegen en zoeken
  2. Prefix zoeken en Starts-With
  3. Wildcard- en regex-zoeken in een trie
  4. Word Search II: trie + backtracking op een grid
← Terug naar Voorbereiding op programmeerinterviews