0Pricing
Coding Interview Prep · Lección

Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary

Aborde de principio a fin dos problemas difíciles —word-ladder-II con BFS y retroceso, y alien-dictionary con ordenación topológica— con una explicación completa.

Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary 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.

Por qué los problemas difíciles son diferentes

Los problemas difíciles de LeetCode se diferencian de los problemas de dificultad media en dos aspectos clave: (1) requieren combinar dos o más técnicas algorítmicas y (2) la solución óptima no suele ser evidente solo a partir del enunciado; debe ver más allá de la descripción superficial para descubrir la estructura subyacente de grafo o de programación dinámica. Word Ladder II y Alien Dictionary son problemas difíciles canónicos que aparecen repetidamente en entrevistas de FAANG.

El enfoque para los problemas difíciles es el siguiente: no intente ver la solución completa desde el principio. En su lugar, divídala en subproblemas, identifique la estructura de cada uno, resuélvalos de forma independiente y después conéctelos. Esta forma de pensar de manera modular es la clave para resolver problemas difíciles bajo presión.

# Hard problem meta-strategy
strategy = [
    '1. Read the problem 2x — hard problems often have subtle constraints',
    '2. Model it as a known structure: graph? DP table? sorted order?',
    '3. Break into sub-problems: separate the graph-building from the traversal',
    '4. Solve sub-problems in order, verifying each before connecting',
    '5. Handle the edge case where no solution exists (empty result, -1, [])',
    '6. Optimise only after the correct but slow solution works',
]
print('Hard problem meta-strategy:')
for step in strategy:
    print(f'  {step}')

Word Ladder II: enunciado del problema

Word Ladder II (LeetCode 126): dadas una palabra inicial, una palabra final y una lista de palabras, encuentre todas las secuencias de transformación más cortas desde el inicio hasta el final. Cada paso transforma exactamente un carácter y cada palabra intermedia debe estar en la lista de palabras. Es estrictamente más difícil que Word Ladder I (que encuentra un solo camino más corto), porque debe enumerar todos los caminos óptimos.

Ejemplo: beginWord='hit', endWord='cog', wordList=['hot','dot','dog','lot','log','cog'] → [['hit','hot','dot','dog','cog'],['hit','hot','lot','log','cog']]. Ambos tienen longitud 5.

# Word Ladder II problem breakdown
begin_word = 'hit'
end_word = 'cog'
word_list = ['hot','dot','dog','lot','log','cog']

# What we need:
# 1. Build a graph: word -> set of words that differ by one character
# 2. BFS to find the MINIMUM number of steps (shortest path distance)
# 3. DFS/backtracking to enumerate ALL paths of that minimum length

# Key insight: BFS finds shortest distance; DFS reconstructs all shortest paths
# Two-phase approach:
print('Phase 1: BFS from begin_word to find min distance to each word')
print('Phase 2: DFS/backtrack from end_word using only edges that decrease distance')
print()
print(f'Input: {begin_word} -> {end_word}')
print(f'Word list: {word_list}')
print('Expected: [[hit,hot,dot,dog,cog],[hit,hot,lot,log,cog]]')

Word Ladder II: fase BFS

En la fase 1, ejecute una BFS nivel por nivel desde la palabra inicial. En cada nivel, encontramos todos los vecinos (palabras que difieren en un carácter). Registramos el nivel (distancia desde el inicio) en el que se alcanza cada palabra por primera vez. NO nos detenemos cuando llegamos a la palabra final: continuamos hasta completar el nivel en el que se encontró end_word, para asegurarnos de explorar todos los caminos más cortos.

Es fundamental construir un diccionario parents que relacione cada palabra con el conjunto de palabras que pueden precederla en cualquier camino más corto. Este es el grafo que utilizamos en la fase 2 para retroceder.

from collections import defaultdict, deque

def find_parents(begin, end, word_set):
    parents = defaultdict(set)
    layer = {begin}
    found = False

    while layer and not found:
        next_layer = set()
        for word in layer:
            for i in range(len(word)):
                for c in 'abcdefghijklmnopqrstuvwxyz':
                    new_word = word[:i] + c + word[i+1:]
                    if new_word in word_set and new_word not in parents:
                        next_layer.add(new_word)
                        parents[new_word].add(word)
                        if new_word == end:
                            found = True
        layer = next_layer
    return parents if found else {}

words = {'hot','dot','dog','lot','log','cog'}
parents = find_parents('hit', 'cog', words)
print('Parents map (which words can precede each word):')
for word, preds in sorted(parents.items()):
    print(f'  {word}: {preds}')

Word Ladder II: fase de retroceso con DFS

En la fase 2, realice un retroceso con DFS desde la palabra final, siguiendo el mapa parents en sentido inverso. Construimos los caminos desde el final hasta el inicio (y después los invertimos). Cuando llegamos a la palabra inicial, hemos encontrado un camino más corto completo. El mapa de parents garantiza que todos los caminos encontrados tienen la longitud mínima: no podemos «desviarnos» hacia un camino más largo.

Este enfoque en dos fases (BFS para los niveles y DFS para reconstruir los caminos) es la solución estándar y se ejecuta en O(n × L × 26) para BFS, donde n = tamaño de la lista de palabras y L = longitud de cada palabra, más O(K × L) para DFS, donde K = número de caminos más cortos.

def find_ladders(beginWord, endWord, wordList):
    word_set = set(wordList)
    if endWord not in word_set:
        return []

    # Phase 1: BFS to build parents map
    parents = defaultdict(set)
    layer = {beginWord}
    found = False
    visited = {beginWord}

    while layer and not found:
        next_layer = set()
        for word in layer:
            for i in range(len(word)):
                for c in 'abcdefghijklmnopqrstuvwxyz':
                    nw = word[:i] + c + word[i+1:]
                    if nw in word_set and nw not in visited:
                        next_layer.add(nw)
                        parents[nw].add(word)
                        if nw == endWord: found = True
        visited |= next_layer
        layer = next_layer

    # Phase 2: DFS backtrack from endWord to beginWord
    result = []
    def dfs(word, path):
        if word == beginWord:
            result.append(path[::-1])
            return
        for parent in parents[word]:
            dfs(parent, path + [parent])
    dfs(endWord, [endWord])
    return result

print(find_ladders('hit','cog',['hot','dot','dog','lot','log','cog']))

Alien Dictionary: enunciado del problema

Alien Dictionary (LeetCode 269): dada una lista de palabras ordenadas lexicográficamente en un idioma alienígena, determine el orden de los caracteres de ese idioma. Devuelva el orden de los caracteres como una cadena. Si no existe un orden válido (hay contradicciones), devuelva una cadena vacía.

Ejemplo: ['wrt','wrf','er','ett','rftt'] → 'wertf'. Al comparar palabras adyacentes: 't' < 'f' (de wrt y wrf), 'w' < 'e' (de wrt y er), 'r' < 't' (de er y ett), 'e' < 'r' (de ett y rftt). Esto es una ordenación topológica de estas restricciones de orden de los caracteres.

words = ['wrt', 'wrf', 'er', 'ett', 'rftt']
# Compare adjacent pairs to extract ordering:
# wrt vs wrf: first diff at index 2: t < f  (t comes before f)
# wrf vs er:  first diff at index 0: w < e  (w comes before e)
# er  vs ett: first diff at index 1: r < t  (r comes before t)
# ett vs rftt:first diff at index 0: e < r  (e comes before r)

ordering_constraints = [
    ('t', 'f', 'from wrt vs wrf'),
    ('w', 'e', 'from wrf vs er'),
    ('r', 't', 'from er vs ett'),
    ('e', 'r', 'from ett vs rftt'),
]
print('Ordering constraints extracted from adjacent word pairs:')
for a, b, source in ordering_constraints:
    print(f'  {a} -> {b}  ({source})')
print('\nThis is a directed graph: find topological order = alien alphabet order')

Alien Dictionary: construcción del grafo

El primer paso es extraer las restricciones: compare cada par de palabras adyacentes, encuentre el primer carácter diferente y añada una arista dirigida del carácter menor al mayor. Si una palabra es un prefijo de la siguiente pero es más larga (por ejemplo, «abc» aparece antes que «ab»), la entrada no es válida; devuelva una cadena vacía inmediatamente.

Todos los caracteres que aparecen en la lista de palabras son nodos del grafo, aunque no tengan restricciones de orden. Estos nodos aislados pueden aparecer en cualquier posición del orden final.

from collections import defaultdict

def build_alien_graph(words):
    adj = defaultdict(set)    # char -> set of chars that come after it
    in_degree = {c: 0 for word in words for c in word}

    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i+1]
        min_len = min(len(w1), len(w2))
        found_diff = False
        for j in range(min_len):
            if w1[j] != w2[j]:
                if w2[j] not in adj[w1[j]]:   # avoid duplicate edges
                    adj[w1[j]].add(w2[j])
                    in_degree[w2[j]] += 1
                found_diff = True
                break
        if not found_diff and len(w1) > len(w2):
            return {}, {}   # invalid: 'abc' before 'ab'
    return adj, in_degree

words = ['wrt', 'wrf', 'er', 'ett', 'rftt']
adj, in_degree = build_alien_graph(words)
print('Adjacency list (directed):', {k: list(v) for k, v in adj.items()})
print('In-degrees:', in_degree)

Alien Dictionary: ordenación topológica

Una vez construido el grafo, aplique la ordenación topológica BFS de Kahn: inicialice una cola con todos los caracteres cuyo grado de entrada sea 0 (sin prerrequisitos). Procese cada carácter y reduzca el grado de entrada de sus sucesores. Cuando el grado de entrada de un sucesor llegue a 0, añádalo a la cola. Recopile los caracteres en el orden de procesamiento: este es el orden alfabético del idioma alienígena.

Si el resultado contiene todos los caracteres, tenemos un orden válido. Si contiene menos caracteres de los esperados, hay un ciclo: las restricciones son contradictorias y devolvemos una cadena vacía.

from collections import deque, defaultdict

def alien_order(words):
    adj = defaultdict(set)
    in_degree = {c: 0 for word in words for c in word}

    for i in range(len(words) - 1):
        w1, w2 = words[i], words[i + 1]
        min_len = min(len(w1), len(w2))
        found = False
        for j in range(min_len):
            if w1[j] != w2[j]:
                if w2[j] not in adj[w1[j]]:
                    adj[w1[j]].add(w2[j])
                    in_degree[w2[j]] += 1
                found = True; break
        if not found and len(w1) > len(w2):
            return ''    # invalid: 'abc' before 'ab'

    # Kahn's BFS topological sort
    queue = deque([c for c in in_degree if in_degree[c] == 0])
    result = []
    while queue:
        c = queue.popleft()
        result.append(c)
        for neighbor in sorted(adj[c]):   # sort for determinism
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return ''.join(result) if len(result) == len(in_degree) else ''

print(alien_order(['wrt','wrf','er','ett','rftt']))  # e.g., 'wertf'
print(alien_order(['z','x']))                         # 'zx'
print(alien_order(['z','x','z']))                     # '' (cycle z->x->z)

Gestión de casos límite: ambos problemas

Tanto Word Ladder II como Alien Dictionary tienen casos límite sutiles que provocan respuestas incorrectas si no se gestionan:

  • Word Ladder II: beginWord y endWord son iguales (devuelva [[beginWord]] o una longitud de 1). endWord no está en wordList (devuelva una cadena vacía). No existe ningún camino (devuelva una cadena vacía).
  • Alien Dictionary: palabras duplicadas (no extraiga ninguna restricción). Una sola palabra (devuelva todos los caracteres únicos). Ciclo en las restricciones (devuelva ''). Una palabra es un prefijo de la siguiente y es más larga (entrada no válida, devuelva ''). Todos los caracteres están aislados (devuelva cualquier orden).
# Edge case tests for Word Ladder II
def test_word_ladder_edge_cases():
    from collections import defaultdict
    def find_ladders(begin, end, word_list):
        # [abbreviated implementation for testing]
        if end not in word_list: return []
        if begin == end: return [[begin]]
        return []  # placeholder

    tests = [
        ('hit', 'cog', ['hot','dot','dog','lot','log'], []),  # no path (cog missing)
        ('hit', 'hit', ['hit'], [['hit']]),                   # begin==end
        ('a',   'c',  ['a','b','c'], [['a','c']]),            # short words
    ]
    for begin, end, wl, expected in tests:
        result = find_ladders(begin, end, wl)
        print(f'{begin}->{end}: result={result}')

# Edge case tests for Alien Dictionary
def test_alien_edge_cases():
    from collections import defaultdict, deque
    # (using alien_order from previous scene)
    tests = [
        (['abc', 'ab'], ''),          # 'abc' before 'ab' = invalid
        (['a'],         'a'),          # single word
        (['z','z'],     'z'),          # duplicate: no constraint
    ]
    print('Alien dictionary edge cases:')
    for words, expected in tests:
        print(f'  {words} -> expected: "{expected}"')

test_word_ladder_edge_cases()
test_alien_edge_cases()

Análisis de complejidad: ambos problemas

Complejidad de Word Ladder II: la fase BFS se ejecuta en O(n × L × 26), donde n = número de palabras de la lista y L = longitud de cada palabra. Para cada palabra de cada nivel BFS, generamos 26L palabras candidatas y comprobamos su pertenencia al conjunto de palabras (O(1) por comprobación). La fase DFS es O(K × L), donde K = número de caminos más cortos (puede ser exponencial en teoría).

Complejidad de Alien Dictionary: construir el grafo cuesta O(C), donde C = número total de caracteres de todas las palabras. La ordenación topológica cuesta O(V + E), donde V = caracteres únicos y E = restricciones de orden. En total, O(C), que equivale a O del número total de caracteres de la entrada.

# Complexity analysis for both problems
complexities = [
    {
        'problem': 'Word Ladder II',
        'time': 'O(n * L * 26) BFS + O(K * L) DFS backtracking',
        'space': 'O(n * L) for word set + parents map',
        'notes': 'K (number of shortest paths) can be exponential in pathological cases',
    },
    {
        'problem': 'Alien Dictionary',
        'time': 'O(C) where C = total characters in all words',
        'space': 'O(V + E) for adjacency list',
        'notes': 'V <= 26 (alphabet), E <= V^2 = 676; often treated as O(C) total',
    },
]
for c in complexities:
    print(f'{c["problem"]}:')
    print(f'  Time:  {c["time"]}')
    print(f'  Space: {c["space"]}')
    print(f'  Notes: {c["notes"]}')
    print()

Resumen de patrones: dos plantillas reutilizables

Ambos problemas enseñan patrones reutilizables. Word Ladder II = BFS para las distancias + DFS para reconstruir los caminos: este patrón aparece siempre que necesita encontrar todos los caminos más cortos en un grafo no ponderado. Construya el mapa de padres durante la BFS y después retroceda desde el destino hasta el origen.

Alien Dictionary = extracción de aristas + ordenación topológica: este patrón aparece siempre que recibe una secuencia ordenada y debe deducir las reglas de orden subyacentes. Extraiga las restricciones dirigidas de los pares adyacentes y después aplique el algoritmo de Kahn. Devuelva '' al detectar un ciclo (orden imposible).

# Pattern templates
print('Template 1: All Shortest Paths in Unweighted Graph')
template_1 = '''
1. BFS from source, recording parents[node] = set of nodes that lead to node
2. Continue each BFS level fully (do not stop at first endNode reach)
3. DFS backtrack from endNode, following parents map
4. Reverse each path found (built end->start, need start->end)
'''
print(template_1)

print('Template 2: Infer Ordering from Sorted Sequence')
template_2 = '''
1. Compare adjacent pairs, extract first differing element as directed constraint
2. Build adjacency list + in-degree map
3. Check for invalid input (prefix longer than successor)
4. Kahn's BFS topological sort
5. If result length < number of nodes => cycle => return invalid
'''
print(template_2)

Cómo ganar confianza con los problemas difíciles

Los problemas difíciles parecen imposibles al principio, pero se vuelven abordables con el modelo mental adecuado. Estas son las ideas clave:

  • Separe las responsabilidades: resuelva cada subproblema de forma independiente antes de conectarlos
  • Conozca sus bloques de construcción: BFS/DFS, ordenación topológica, Dijkstra y tablas de DP; los problemas difíciles combinan estos elementos de maneras que no siempre son evidentes
  • Empiece por los ejemplos: siga el problema manualmente con un ejemplo pequeño para descubrir la estructura subyacente
  • Verifique los subproblemas: después de implementar la fase 1 (construcción del grafo), imprima el grafo y verifíquelo manualmente antes de continuar con la fase 2
# Hard problem confidence-building practice plan
practice_plan = [
    ('Week 1', 'BFS/DFS fundamentals', ['Number of Islands', 'Clone Graph', 'Word Ladder I']),
    ('Week 2', 'Topological sort', ['Course Schedule I & II', 'Alien Dictionary (easy)']),
    ('Week 3', 'All-paths problems', ['All Paths to Target', 'Word Ladder II (hard)']),
    ('Week 4', 'Hard combos', ['Minimum Window Substring', 'Serialize/Deserialize Tree']),
]
print('4-week hard problem practice plan:')
for week, theme, problems in practice_plan:
    print(f'\n{week} — {theme}:')
    for p in problems:
        print(f'  - {p}')

print('\nAfter each problem, write:')
print('  1. The pattern it belongs to')
print('  2. The 2-3 key sub-problems')
print('  3. One insight you would not have had before solving it')

Comprobación rápida

Compruebe su comprensión de los conceptos de Estructuras de datos & algoritmos: preparación para entrevistas de programación de esta lección.

Resumen de la lección

En esta lección ha aprendido: Word Ladder II utiliza BFS para construir un mapa de parents con todos los predecesores de los caminos más cortos y después retroceso con DFS para enumerar todos los caminos más cortos siguiendo parents desde el final hasta el inicio, Alien Dictionary extrae restricciones dirigidas de pares de palabras adyacentes y aplica la ordenación topológica de Kahn para ordenar los caracteres, devolviendo una cadena vacía al detectar un ciclo, y los problemas difíciles se dividen en varios subproblemas —construir el grafo, encontrar las distancias y reconstruir los caminos—, cada uno resuelto de forma independiente con algoritmos conocidos. Ahora ha completado el curso completo de preparación para entrevistas de DSA. Aplique en sus entrevistas todos los patrones y técnicas de este itinerario con confianza.

Preguntas frecuentes

¿La lección «Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary» es gratis?

Sí — el texto completo de «Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary» 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 «Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary»?

Aborde de principio a fin dos problemas difíciles —word-ladder-II con BFS y retroceso, y alien-dictionary con ordenación topológica— con una explicación completa. 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 «Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary»?

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

  1. Guía rápida de reconocimiento de patrones
  2. Entrevista simulada cronometrada: problemas fáciles y medios
  3. Gestión de casos límite y comunicación con el entrevistador
  4. Resolución guiada de problemas difíciles: Word Ladder II y Alien Dictionary
← Volver a Coding Interview Prep