Word Search II: trie y retroceso en una cuadrícula
Inserte todas las palabras objetivo en un trie y ejecute DFS con retroceso sobre un tablero 2D para encontrar simultáneamente todas las palabras válidas en O(m × n × 4^L).
Word Search II: trie y retroceso en una cuadrícula es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
El problema de Word Search II
Word Search II (LeetCode 212): dado un tablero de caracteres m × n y una lista de palabras, encuentre todas las palabras que se puedan formar mediante celdas adyacentes consecutivas (horizontal o verticalmente), donde cada celda solo se puede utilizar una vez. Esto es más difícil que Word Search I (una sola palabra), porque necesitamos encontrar todas las palabras coincidentes simultáneamente; ejecutar Word Search I de forma ingenua para cada palabra tiene un coste de O(W × m × n × 4^L), que es demasiado lento.
¿Por qué combinar trie y backtracking?
Insertar todas las palabras objetivo en un trie y después ejecutar un DFS con backtracking sobre el tablero permite buscar todas las palabras simultáneamente. En cada celda del tablero, en lugar de comprobar «¿esta ruta forma mi palabra objetivo?», comprobamos «¿esta ruta coincide con un prefijo del trie?». En cuanto un prefijo del trie deja de coincidir, podamos toda la rama del DFS, lo que evita trabajo redundante entre todas las palabras que comparten ese prefijo.
Construir el trie a partir de una lista de palabras
Inserte todas las palabras en un trie. Almacene la palabra completa en el nodo hoja (en node.word) en lugar de guardar únicamente un booleano, de modo que, cuando se encuentre una coincidencia completa durante el backtracking, podamos añadir inmediatamente la palabra a los resultados sin reconstruirla carácter por carácter.
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')Backtracking con DFS en la cuadrícula
Inicie un DFS desde cada celda del tablero. En cada paso: (1) compruebe si el carácter de la celda actual existe como hijo del nodo actual del trie; (2) si existe, marque la celda como visitada (asígnеле un marcador como '#') y haga una llamada recursiva para las 4 celdas vecinas; (3) después de la recursión, restaure la celda (quite la marca). Cuando un nodo del trie tenga un word distinto de None, añádalo a los resultados y establézcalo en None para evitar duplicados.
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']Análisis de complejidad
Tiempo: O(m × n × 4^L), donde L es la longitud máxima de las palabras. Para cada una de las m×n celdas iniciales, el DFS explora hasta 4^L rutas. El trie poda las rutas que no coinciden con ningún prefijo de palabra, por lo que en la práctica es mucho más rápido. Construir el trie cuesta O(W × L), donde W es el número de palabras. Espacio: O(W × L) para el trie, más una profundidad O(L) de la pila de recursión.
Poda: eliminar nodos hoja después de encontrar una palabra
Después de encontrar una palabra, elimine el nodo hoja del trie (no se limite a establecer la palabra en null) si no tiene hijos. Esto evita volver a visitar ramas muertas en llamadas posteriores del DFS. Cuando los hijos de un nodo queden vacíos después de encontrar la palabra, elimínelo del diccionario children de su padre. Esta optimización es importante cuando muchas palabras comparten prefijos largos.
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')Por qué es mejor almacenar word en el nodo
Almacenar la palabra completa en el nodo hoja del trie (en lugar de reconstruirla a partir de la ruta del DFS) ofrece dos ventajas: (1) recuperar la palabra en O(1) cuando se encuentra una coincidencia, en lugar de reconstruir la ruta en O(L); (2) establecer node.word = None después de encontrar la palabra proporciona una deduplicación limpia en O(1), sin necesidad de un conjunto de resultados independiente. En Word Search II, en particular, evitar duplicados es importante porque, en teoría, la misma palabra podría encontrarse siguiendo rutas diferentes.
Marcar celdas visitadas directamente en el tablero
En lugar de utilizar un conjunto visited independiente (que requeriría un espacio O(m × n) por ruta del DFS), marque las celdas directamente en el tablero reemplazando su carácter por un marcador como '#'. Cuando termine el DFS, restaure el carácter original. Esta técnica: (1) utiliza un espacio adicional O(1) por celda; (2) evita automáticamente volver a visitar una celda dentro de una misma ruta; (3) es completamente transparente para el recorrido del trie, ya que '#' nunca estará en el trie.
Casos límite que se deben gestionar
Casos límite importantes: (1) palabras duplicadas en la lista de palabras: almacénelas en un conjunto o utilice la técnica node.word = None para evitar duplicados en los resultados; (2) palabras muy largas que superan las dimensiones del tablero: no se pueden formar, pero el DFS lo gestiona de forma natural al quedarse sin celdas adyacentes; (3) tablero de una sola celda: solo se pueden encontrar palabras de un carácter; (4) una misma palabra se puede encontrar mediante rutas diferentes: la técnica node.word = None evita contarla dos veces.
Comparación con el enfoque ingenuo
Enfoque ingenuo: para cada una de las W palabras, ejecute Word Search I: O(W × m × n × 4^L). Con el trie, todas las palabras se buscan simultáneamente: O(m × n × 4^L), independientemente de W. Para W=1000 palabras de longitud 10 en un tablero de 10×10, el enfoque ingenuo es 1000 veces más lento que el trie. El trie actúa como un filtro de prefijos compartido que amortiza el coste entre todas las palabras: un ejemplo clásico de cómo utilizar una estructura de datos para lograr una mejora asintótica.
Resumen completo de la solución
Solución completa de Word Search II: construya un trie con las palabras y almacene la cadena de la palabra en la hoja. Para cada celda del tablero, ejecute un DFS: compruebe si el carácter actual existe en el nodo actual del trie, marque la celda como '#', haga llamadas recursivas para las 4 celdas vecinas y restaure la celda. Cuando node.word no sea null, añádalo a los resultados y establézcalo en null. Opcionalmente, pode las ramas vacías del trie después de utilizarlas. Devuelva la lista de resultados. Tiempo: O(m×n×4^L); espacio: trie O(W×L) + recursión O(L).
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 resultComprobación rápida
Ponga a prueba su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.
Resumen de la lección
En esta lección aprendió: Word Search II utiliza un trie para permitir la búsqueda simultánea de varias palabras con poda de prefijos compartidos, almacenar la cadena de la palabra en la hoja del trie permite recuperarla en O(1) y deduplicarla fácilmente estableciéndola en None después de encontrarla, y marcar las celdas visitadas directamente en el tablero con '#' evita utilizar un espacio adicional O(m×n) por ruta del DFS. Con esto termina el curso Tries and String Algorithms: ha aprendido a dominar una de las estructuras de datos específicas para cadenas más potentes y utilizadas en entrevistas.
Preguntas frecuentes
¿La lección «Word Search II: trie y retroceso en una cuadrícula» es gratis?
Sí — el texto completo de «Word Search II: trie y retroceso en una cuadrícula» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Word Search II: trie y retroceso en una cuadrícula»?
Inserte todas las palabras objetivo en un trie y ejecute DFS con retroceso sobre un tablero 2D para encontrar simultáneamente todas las palabras válidas en O(m × n × 4^L). Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Word Search II: trie y retroceso en una cuadrícula»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Clase TrieNode: inserción y búsqueda
- Búsqueda por prefijo y Starts-With
- Búsqueda con comodines y expresiones regulares en un trie
- Word Search II: trie y retroceso en una cuadrícula