0Pricing
DSA Interview Prep · Lezione

Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary

Affronti dall'inizio alla fine due problemi difficili — word-ladder-II con BFS e backtracking e alien-dictionary con ordinamento topologico — con una spiegazione completa.

Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary è una lezione DSA Interview Prep gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DSA Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DSA Interview Prep include 4 lezioni in totale.

Perché i problemi difficili sono diversi

I problemi difficili di LeetCode differiscono da quelli di difficoltà media in due aspetti fondamentali: (1) richiedono di combinare due o più tecniche algoritmiche e (2) la soluzione ottimale spesso non è evidente dal solo testo del problema: è necessario andare oltre la descrizione superficiale per individuare la struttura sottostante del grafo o della programmazione dinamica. Word Ladder II e Alien Dictionary sono problemi difficili canonici che ricorrono frequentemente nei colloqui FAANG.

L'approccio ai problemi difficili è il seguente: non cerchi di vedere subito la soluzione completa. Suddivida invece il problema in sottoproblemi, individui la struttura di ciascun sottoproblema, risolva ogni parte separatamente e poi le colleghi. Questo modo di ragionare per moduli è la chiave per risolvere problemi difficili sotto pressione.

# 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: descrizione del problema

Word Ladder II (LeetCode 126): data una parola iniziale, una parola finale e un elenco di parole, trovi tutte le sequenze di trasformazione più brevi dalla parola iniziale a quella finale. Ogni passaggio modifica esattamente un carattere e ogni parola intermedia deve appartenere all'elenco di parole. Questo problema è nettamente più difficile di Word Ladder I, che trova un solo percorso più breve, perché è necessario elencare tutti i percorsi ottimali.

Esempio: beginWord='hit', endWord='cog', wordList=['hot','dot','dog','lot','log','cog'] → [['hit','hot','dot','dog','cog'],['hit','hot','lot','log','cog']]. Entrambi hanno lunghezza 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

Nella Fase 1, esegua la BFS a livelli a partire dalla parola iniziale. A ogni livello, trovi tutti i vicini, cioè le parole che differiscono per un carattere. Registri il livello, ovvero la distanza dalla parola iniziale, al quale ogni parola viene raggiunta per la prima volta. NON si fermi quando raggiunge la parola finale: continui fino a completare il livello in cui è stato trovato end_word, per assicurarsi di esplorare tutti i percorsi più brevi.

È fondamentale costruire un dizionario parents che associ ogni parola all'insieme delle parole che possono precederla in un qualsiasi percorso più breve. Questo è il grafo che utilizzeremo nella Fase 2 per il backtracking.

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 di backtracking con DFS

Nella Fase 2, esegua il backtracking con DFS a partire dalla parola finale, seguendo la mappa parents al contrario. Costruiamo i percorsi dalla fine all'inizio e poi li invertiamo. Quando raggiungiamo la parola iniziale, abbiamo trovato un percorso più breve completo. La mappa parents garantisce che tutti i percorsi trovati abbiano lunghezza minima: non possiamo «deviare» verso un percorso più lungo.

Questo approccio in due fasi, BFS per i livelli e DFS per la ricostruzione dei percorsi, è la soluzione standard ed esegue in O(n × L × 26) per la BFS, dove n = dimensione dell'elenco di parole e L = lunghezza delle parole, oltre a O(K × L) per la DFS, dove K = numero di percorsi più brevi.

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: descrizione del problema

Alien Dictionary (LeetCode 269): data una lista di parole ordinate lessicograficamente in una lingua aliena, determini l'ordine dei caratteri in quella lingua. Restituisca l'ordinamento dei caratteri come stringa. Se non esiste alcun ordinamento valido, perché i vincoli sono contraddittori, restituisca una stringa vuota.

Esempio: ['wrt','wrf','er','ett','rftt'] → 'wertf'. Confrontando le parole adiacenti: 't' < 'f' (da wrt e wrf), 'w' < 'e' (da wrt ed er), 'r' < 't' (da er ed ett), 'e' < 'r' (da ett e rftt). Si tratta di un ordinamento topologico di questi vincoli sull'ordinamento dei caratteri.

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: costruzione del grafo

Il primo passaggio consiste nell'estrarre i vincoli: confronti ogni coppia adiacente di parole, trovi il primo carattere diverso e aggiunga un arco orientato dal carattere minore a quello maggiore. Se una parola è un prefisso della parola successiva ma è più lunga, ad esempio 'abc' prima di 'ab', l'input non è valido: restituisca immediatamente una stringa vuota.

Tutti i caratteri presenti nell'elenco di parole sono nodi del grafo, anche se non hanno vincoli di ordinamento. Questi nodi isolati possono comparire in qualsiasi posizione nell'ordinamento finale.

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: ordinamento topologico

Una volta costruito il grafo, applichi l'ordinamento topologico BFS di Kahn: inizializzi una coda con tutti i caratteri con grado entrante pari a 0, cioè senza prerequisiti. Elabori ogni carattere e diminuisca il grado entrante dei suoi successori. Quando il grado entrante di un successore raggiunge 0, lo inserisca nella coda. Raccolga i caratteri nell'ordine di elaborazione: questo è l'ordine alfabetico della lingua aliena.

Se il risultato contiene tutti i caratteri, l'ordinamento è valido. Se contiene meno caratteri del previsto, esiste un ciclo: i vincoli sono contraddittori e occorre restituire una stringa vuota.

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)

Gestione dei casi limite: entrambi i problemi

Sia Word Ladder II sia Alien Dictionary presentano casi limite non evidenti che possono causare risposte errate se non vengono gestiti:

  • Word Ladder II: beginWord ed endWord coincidono, quindi restituisca [[beginWord]] o una sequenza di lunghezza 1. endWord non è presente in wordList, quindi restituisca una sequenza vuota. Non esiste alcun percorso, quindi restituisca una sequenza vuota.
  • Alien Dictionary: parole duplicate, dalle quali non si estrae alcun vincolo. Una sola parola, nel qual caso restituisca tutti i caratteri distinti. Un ciclo nei vincoli, nel qual caso restituisca ''. Una parola è un prefisso della successiva ma è più lunga, quindi l'input non è valido e occorre restituire ''. Tutti i caratteri sono isolati, quindi è possibile restituire un ordine qualsiasi.
# 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()

Analisi della complessità: entrambi i problemi

Complessità di Word Ladder II: la fase BFS esegue in O(n × L × 26), dove n = numero di parole nell'elenco e L = lunghezza delle parole. Per ogni parola a ogni livello BFS, generiamo 26L parole candidate e verifichiamo l'appartenenza all'insieme delle parole, con costo O(1) per verifica. La fase DFS esegue in O(K × L), dove K = numero di percorsi più brevi, che in teoria può essere esponenziale.

Complessità di Alien Dictionary: la costruzione del grafo esegue in O(C), dove C = numero totale di caratteri in tutte le parole. L'ordinamento topologico esegue in O(V + E), dove V = caratteri distinti ed E = vincoli di ordinamento. Complessivamente, la complessità è O(C), ovvero O(numero totale di caratteri nell'input).

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

Riepilogo dei pattern: due modelli riutilizzabili

Entrambi i problemi insegnano pattern riutilizzabili. Word Ladder II = BFS per le distanze + DFS per la ricostruzione dei percorsi: questo pattern compare ogni volta che servono tutti i percorsi più brevi in un grafo non pesato. Costruisca la mappa dei predecessori durante la BFS, quindi esegua il backtracking dalla destinazione alla sorgente.

Alien Dictionary = estrazione degli archi + ordinamento topologico: questo pattern compare ogni volta che viene fornita una sequenza ordinata e occorre dedurre le regole di ordinamento sottostanti. Estragga i vincoli orientati dalle coppie adiacenti, quindi applichi l'algoritmo di Kahn. Restituisca '' quando viene rilevato un ciclo, perché l'ordinamento è impossibile.

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

Acquisire sicurezza nei problemi difficili

I problemi difficili sembrano impossibili all'inizio, ma diventano affrontabili con il giusto modello mentale. Gli insegnamenti principali sono:

  • Separi le responsabilità: risolva ogni sottoproblema in modo indipendente prima di collegarli
  • Conosca i propri strumenti di base: BFS/DFS, ordinamento topologico, Dijkstra, tabelle DP; i problemi difficili combinano questi elementi in modi non evidenti
  • Inizi dagli esempi: analizzi manualmente il problema con un esempio piccolo per scoprire la struttura sottostante
  • Verifichi i sottoproblemi: dopo aver implementato la Fase 1, cioè la costruzione del grafo, stampi il grafo e lo verifichi manualmente prima di procedere alla 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')

Verifica rapida

Verifichi la Sua comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep trattati in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che: Word Ladder II utilizza la BFS per costruire una mappa parents di tutti i predecessori dei percorsi più brevi, quindi il backtracking con DFS per elencare tutti i percorsi più brevi seguendo parents dalla fine all'inizio, Alien Dictionary estrae vincoli orientati dalle coppie di parole adiacenti e applica l'ordinamento topologico di Kahn per ordinare i caratteri, restituendo una stringa vuota quando viene rilevato un ciclo e i problemi difficili si scompongono in più sottoproblemi, come costruire il grafo, trovare le distanze e ricostruire i percorsi, ciascuno dei quali viene risolto separatamente con algoritmi conosciuti. A questo punto ha completato l'intero corso DSA Interview Prep. Applichi ogni pattern e tecnica di questo percorso ai Suoi colloqui con sicurezza.

Domande Frequenti

La lezione «Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary» è gratuita?

Sì — il testo completo di «Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DSA Interview Prep, passa a CoddyKit PRO. Il corso DSA Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary»?

Affronti dall'inizio alla fine due problemi difficili — word-ladder-II con BFS e backtracking e alien-dictionary con ordinamento topologico — con una spiegazione completa. Eserciti DSA Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare DSA Interview Prep?

Non è richiesta alcuna esperienza precedente. DSA Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione DSA Interview Prep?

Sì. Ogni lezione DSA Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Scheda di riferimento per il riconoscimento dei pattern
  2. Colloquio simulato a tempo: problemi facili e medi
  3. Gestione dei casi limite e comunicazione durante il colloquio
  4. Analisi guidata di problemi difficili: Word Ladder II e Alien Dictionary
← Torna a DSA Interview Prep