0Pricing
Coding Interview Prep · Aula

Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena

Resolva de ponta a ponta dois problemas difíceis — escada de palavras II com BFS + retrocesso e dicionário alienígena com ordenação topológica — com uma explicação completa.

Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.

Por que os problemas difíceis são diferentes

Problemas difíceis do LeetCode diferem dos problemas médios de duas maneiras principais: (1) exigem a combinação de duas ou mais técnicas algorítmicas e (2) a solução ideal geralmente não é evidente apenas pelo enunciado — é preciso enxergar além da descrição superficial e identificar a estrutura subjacente de grafo ou DP. Escada de Palavras II e Dicionário Alienígena são problemas difíceis clássicos que aparecem repetidamente em entrevistas da FAANG.

A abordagem para problemas difíceis é não tentar descobrir a solução completa de uma só vez. Em vez disso, divida o problema em subproblemas, identifique a estrutura de cada um, resolva-os separadamente e depois conecte-os. Esse pensamento modular é a chave para resolver problemas difíceis sob pressão.

# 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}')

Escada de Palavras II: enunciado do problema

Escada de Palavras II (LeetCode 126): dada uma palavra inicial, uma palavra final e uma lista de palavras, encontre todas as sequências de transformação mais curtas da palavra inicial até a final. Cada etapa transforma exatamente um caractere, e cada palavra intermediária deve estar na lista de palavras. Este problema é consideravelmente mais difícil que Escada de Palavras I (que encontra apenas um caminho mais curto), pois é preciso enumerar todos os caminhos ideais.

Exemplo: beginWord='hit', endWord='cog', wordList=['hot','dot','dog','lot','log','cog'] → [['hit','hot','dot','dog','cog'],['hit','hot','lot','log','cog']]. Ambos têm comprimento 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]]')

Escada de Palavras II: etapa BFS

Na etapa 1, execute BFS nível a nível a partir da palavra inicial. Em cada nível, encontre todos os vizinhos (palavras que diferem em um caractere). Registre o nível (a distância desde o início) em que cada palavra é alcançada pela primeira vez. Não pare ao alcançar a palavra final — continue até concluir o nível em que a palavra final foi encontrada, para garantir que todos os caminhos mais curtos sejam explorados.

O mais importante é criar um dicionário parents que associe cada palavra ao conjunto de palavras que podem precedê-la em qualquer caminho mais curto. Esse é o grafo usado na etapa 2 para o retrocesso.

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}')

Escada de Palavras II: etapa de retrocesso com DFS

Na etapa 2, use retrocesso com DFS a partir da palavra final, seguindo o mapa parents no sentido inverso. Crie os caminhos da palavra final até a inicial (e depois inverta-os). Quando chegar à palavra inicial, você terá encontrado um caminho mais curto completo. O mapa de predecessores garante que todos os caminhos encontrados tenham comprimento mínimo — não é possível 'desviar' para um caminho mais longo.

Essa abordagem em duas etapas (BFS para os níveis e DFS para a reconstrução dos caminhos) é a solução padrão e executa em O(n × L × 26) para BFS, onde n = tamanho da lista de palavras e L = comprimento da palavra, além de O(K × L) para DFS, onde K = número de caminhos mais curtos.

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']))

Dicionário Alienígena: enunciado do problema

Dicionário Alienígena (LeetCode 269): dada uma lista de palavras ordenadas lexicograficamente em um idioma alienígena, determine a ordem dos caracteres desse idioma. Retorne a ordenação dos caracteres como uma string. Se não existir uma ordenação válida (por haver contradições), retorne uma string vazia.

Exemplo: ['wrt','wrf','er','ett','rftt'] → 'wertf'. Ao comparar palavras adjacentes: 't' < 'f' (de wrt e wrf), 'w' < 'e' (de wrt e er), 'r' < 't' (de er e ett), 'e' < 'r' (de ett e rftt). Isso é uma ordenação topológica dessas restrições de ordenação dos 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')

Dicionário Alienígena: construção do grafo

O primeiro passo é extrair as restrições: compare cada par adjacente de palavras, encontre o primeiro caractere diferente e adicione uma aresta direcionada do caractere menor para o maior. Se uma palavra for prefixo da próxima, mas for mais longa (por exemplo, 'abc' antes de 'ab'), a entrada será inválida — retorne imediatamente uma string vazia.

Todos os caracteres que aparecem na lista de palavras são nós do grafo, mesmo que não tenham restrições de ordenação. Esses nós isolados podem aparecer em qualquer posição na ordenação 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)

Dicionário Alienígena: ordenação topológica

Depois de construir o grafo, aplique a ordenação topológica BFS de Kahn: inicialize uma fila com todos os caracteres cujo grau de entrada é 0 (sem pré-requisitos). Processe cada caractere e diminua o grau de entrada de seus sucessores. Quando o grau de entrada de um sucessor chegar a 0, coloque-o na fila. Reúna os caracteres na ordem de processamento — essa é a ordem alfabética alienígena.

Se o resultado contiver todos os caracteres, teremos uma ordenação válida. Se houver menos caracteres que o esperado, existe um ciclo — as restrições são contraditórias e retornamos uma string vazia.

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)

Tratamento de casos extremos: os dois problemas

Escada de Palavras II e Dicionário Alienígena têm casos extremos sutis que causam respostas incorretas quando não são tratados:

  • Escada de Palavras II: beginWord e endWord são iguais (retorne [[beginWord]] ou um resultado de comprimento 1). endWord não está em wordList (retorne vazio). Não existe caminho (retorne vazio).
  • Dicionário Alienígena: palavras duplicate (não extraia nenhuma restrição). Uma única palavra (retorne todos os caracteres únicos). Ciclo nas restrições (retorne ''). Uma palavra é um prefixo mais longo da próxima (entrada inválida, retorne ''). Todos os caracteres são isolados (retorne qualquer ordem).
# 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álise de complexidade: os dois problemas

Complexidade de Escada de Palavras II: a etapa BFS executa em O(n × L × 26), onde n = número de palavras na lista e L = comprimento da palavra. Para cada palavra em cada nível BFS, geramos 26L palavras candidatas e verificamos sua presença no conjunto de palavras (O(1) por verificação). A etapa DFS é O(K × L), onde K = número de caminhos mais curtos (que pode ser exponencial na teoria).

Complexidade do Dicionário Alienígena: a construção do grafo é O(C), onde C = total de caracteres em todas as palavras. A ordenação topológica é O(V + E), onde V = caracteres únicos e E = restrições de ordenação. No total, O(C), que corresponde a O(total de caracteres da 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()

Resumo dos padrões: dois modelos reutilizáveis

Os dois problemas ensinam padrões reutilizáveis. Escada de Palavras II = BFS para distâncias + DFS para reconstrução de caminhos: esse padrão aparece sempre que você precisa de todos os caminhos mais curtos em um grafo não ponderado. Construa o mapa de predecessores durante a BFS e depois faça o retrocesso do destino até a origem.

Dicionário Alienígena = extração de arestas + ordenação topológica: esse padrão aparece sempre que você recebe uma sequência ordenada e precisa deduzir as regras de ordenação subjacentes. Extraia restrições direcionadas de pares adjacentes e depois aplique o algoritmo de Kahn. Retorne '' ao detectar um ciclo (ordenação impossível).

# 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)

Ganhando confiança com problemas difíceis

Os problemas difíceis parecem impossíveis no início, mas se tornam acessíveis com o modelo mental adequado. Os principais insights são:

  • Separe as responsabilidades: resolva cada subproblema de forma independente antes de conectá-los
  • Conheça seus blocos de construção: BFS/DFS, ordenação topológica, Dijkstra, tabelas de DP — problemas difíceis combinam esses elementos de maneiras pouco óbvias
  • Comece pelos exemplos: percorra o problema manualmente com um exemplo pequeno para descobrir a estrutura subjacente
  • Verifique os subproblemas: depois de implementar a etapa 1 (construção do grafo), exiba o grafo e verifique-o manualmente antes de prosseguir para a etapa 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')

Verificação rápida

Avalie sua compreensão dos conceitos de Estruturas de Dados e Algoritmos — Preparação para Entrevistas de Programação abordados nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu que: Escada de Palavras II usa BFS para construir um mapa de predecessores de todos os caminhos mais curtos e depois faz retrocesso com DFS para enumerar todos os caminhos mais curtos, seguindo os predecessores do fim até o início, Dicionário Alienígena extrai restrições direcionadas de pares adjacentes de palavras e aplica a ordenação topológica de Kahn para ordenar os caracteres, retornando uma string vazia ao detectar um ciclo, e problemas difíceis são decompostos em vários subproblemas — construção do grafo, descoberta de distâncias e reconstrução de caminhos —, cada um resolvido de forma independente com algoritmos conhecidos. Você concluiu agora o curso completo de Preparação para Entrevistas de DSA. Aplique todos os padrões e técnicas desta trilha em suas entrevistas com confiança.

Perguntas Frequentes

A aula “Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena” é grátis?

Sim — o texto completo de “Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.

O que vou aprender em “Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena”?

Resolva de ponta a ponta dois problemas difíceis — escada de palavras II com BFS + retrocesso e dicionário alienígena com ordenação topológica — com uma explicação completa. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Coding Interview Prep?

Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Coding Interview Prep?

Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Guia rápido de reconhecimento de padrões
  2. Entrevista simulada cronometrada: problemas fáceis e médios
  3. Tratamento de casos extremos e comunicação com o entrevistador
  4. Resolução guiada de problemas difíceis: escada de palavras II e dicionário alienígena
← Voltar para Coding Interview Prep