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).
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 resultKorte 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.
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
- TrieNode-klasse: invoegen en zoeken
- Prefix zoeken en Starts-With
- Wildcard- en regex-zoeken in een trie
- Word Search II: trie + backtracking op een grid