Word Ladder
Sana iki kelime, beginWord ve endWord, ayrıca wordList adlı bir kelime listesi verilir. Bir dönüşüm dizisi beginWord ile başlar, endWord ile biter ve her adımda bir kelimeden sonrakine geçerken tam olarak bir harf değişir. beginWord sonrasındaki her kelime wordList içinden gelmelidir; dolayısıyla endWord bu listede bulunmalıdır, ancak beginWord listede bulunmak zorunda değildir.
Her iki ucu da sayarak en kısa dönüşüm dizisindeki kelime sayısını döndür; böyle bir dizi yoksa 0 döndür.
Fonksiyon
- beginWordstring
- merdivenin ilk kelimesi
- endWordstring
- Merdivenin ulaşması gereken kelime
- wordListstring-array
- sonraki her adımın türemesi gereken sözcükler
- Döndürürinteger
- en kısa merdivendeki kelime sayısı veya hiç yoksa 0
Kısıtlar
1 ≤ beginWord.length ≤ 10endWordvewordListiçindeki her kelime,beginWordile aynı uzunluğa sahiptir.1 ≤ wordList.length ≤ 5000- Tüm sözcükler yalnızca küçük harfli İngilizce harfler içerir.
beginWord != endWordwordListiçindeki sözcüklerin hepsi birbirinden farklıdır.beginWordbunlardan biri olabilir ya da olmayabilir.
Örnekler
- Girdi
- beginWord = "lead"endWord = "gold"wordList = ["load", "goad", "gold", "lend", "lewd", "bold"]
- Çıktı
- 4
- Açıklama
leadilegoldüç harfte farklıdır, bu yüzden bir kelime merdiveni en az 4 kelime gerektirir velead,load,goad,gold4 kelimeden oluşur.lendvelewdçıkmaz sokaklardır.
- Girdi
- beginWord = "cat"endWord = "dog"wordList = ["cot", "cog", "dot", "dig"]
- Çıktı
- 0
- Açıklama
cat,cot,cog,dog'a bir harf farkla yaklaşır, ancakdoglistede olmadığı için hiçbir merdiven orada sona eremez.
- Girdi
- beginWord = "ab"endWord = "cd"wordList = ["ab", "cb", "cd", "ad"]
- Çıktı
- 3
- Açıklama
ab,ad,cdveab,cb,cdikisi de 3 kelime içerir.ablistede de yer alır, ancak başlangıç yalnızca bir kez sayılır.
Ek soru
Uzunluğu yerine en kısa merdivenlerden birini, kelimeleri sırayla döndürebilir misin?
İpuçları
Tek tek açın. Her biri biraz daha fazlasını gösterir.
Her kelimeyi bir nokta olarak düşün ve tam olarak bir harf bakımından farklı olan iki kelimenin arasına bir çizgi çiz. Bu görselde merdiven nedir ve en kısa merdiven hangisidir?
En kısa merdiven, en az satır içeren yoldur ve her satır eşit sayılır. Genişlik öncelikli arama, iki adım uzaktaki herhangi bir kelimeye ulaşmadan önce bir adım uzaktaki tüm kelimelere ulaşır; bu nedenle
endWordkelimesine ilk ulaştığında en az adımı kullanmış olur. Bir kelimeye ilk ulaştığın anda onu ziyaret edilmiş olarak işaretle.Komşularını bulmak için bir sözcüğü tüm listeyle karşılaştırmak yavaştır. Bunun yerine, her seferinde bir harfi gizle:
hot,hatvehitsözcüklerinin hepsih*tolur. Her sözcüğü, kalıplarının her birine ait kovaya koy. Bir sözcüğün komşuları, kovalarındaki diğer sözcüklerdir. AramayıbeginWordsözcüğünden başlayarak seviye seviye yürüt ve seviyeleri say.
Çözüm
Kelimeleri bir grafın düğümleri olarak ele al; aralarında bir harf fark olan kelimeler arasında bir kenar olsun. Her kenarın maliyeti aynı olduğundan, genişlik öncelikli arama en kısa merdiveni bulur. Zor olan, kenarları hızlı bulmaktır. Aşağıda n kelime sayısı, L ise kelimelerin uzunluğudur.
Derinlik öncelikli aramayla her merdiveni dene
Doğru, ama en büyük testlerde bitmiyor
Sezgi
Geçerli sözcükten başlayarak, bir harf farkı olan kullanılmamış her sözcüğü dene ve endWord değerine ulaştığında sözcük uzunluğunu koruyarak daha derine in. Yoldaki sözcükleri kullanılmış olarak işaretle ve geri dönerken işaretlerini kaldır. best sözcükten oluşan bir basamak dizisi bulunduğunda, best-1 sözcükten oluşan yolları uzatmayı bırak.
Bu yöntem, tekrarlanan sözcük içermeyen tüm basamak dizilerini dener ve en kısa basamak dizisi hiçbir sözcüğü tekrarlamaz: iki aynı sözcüğün arasındaki kısmı çıkarırsan dizi kısalır.
aaa, baa ile zaa arasındaki 26 sözcüğün tümü bir harf farkıyla ayrılır ve yaklaşık 4 × 10^26 farklı sırada dizilebilir. endWord sözcüğüne ulaşılamıyorsa kesme koşulu hiç devreye girmez ve arama hepsini dener.
Algoritma
beginWordkullanılmış olarak işaretliyse listede bırakın vebestdeğerini 0 olarak ayarlayın.search(word, length):endWordnoktasında,lengthdeğeribestdeğerinden küçüksebestdeğerini güncelleyin ve geri dönün.bestayarlanmışsa velength + 1 ≥ bestise geri dönün.- Aksi takdirde, bir harf farkla eşleşen kullanılmamış her kelime için onu işaretleyin,
search(next, length + 1)çağrısını yapın ve işareti kaldırın. search(beginWord, 1)çağrısından sonrabestdeğerini döndürün.
def ladderLength(beginWord, endWord, wordList):
def one_letter_apart(a, b):
differences = 0
for x, y in zip(a, b):
if x != y:
differences += 1
if differences > 1:
return False
return differences == 1
best = 0 # words in the shortest sequence found so far, 0 while there is none
used = [word == beginWord for word in wordList] # words on the current path
def search(word, length):
nonlocal best
if word == endWord:
if best == 0 or length < best:
best = length
return
if best != 0 and length + 1 >= best:
return # any longer path cannot beat the best one
for i, candidate in enumerate(wordList):
if not used[i] and one_letter_apart(word, candidate):
used[i] = True
search(candidate, length + 1)
used[i] = False # free the word for other paths
search(beginWord, 1)
return bestGenişlik öncelikli arama, her çifti karşılaştırma
Doğru, ama en büyük testlerde bitmiyor
Sezgi
Bir kuyruk, kelimeleri uzaklık sırasına göre işler: uzaklığı d olan tüm kelimeler, uzaklığı d + 1 olan herhangi bir kelimeden önce kuyruktan çıkar. Dolayısıyla bir kelimeye ilk kez d uzaklığında ulaşıldığında, daha yakın tüm kelimeler zaten işlenmiştir ve ona giden daha kısa bir merdiven kelimeye daha önce ulaşmış olurdu. Kelimenin uzaklığı kuyruğa eklendiği anda kesinleşir; bu yüzden o zaman ziyaret edildi olarak işaretle ve endWord ilk kez ortaya çıktığında, uzaklığı yanıttır.
Bu sürüm, listedeki her kelimeyle karşılaştırarak bir kelimenin komşularını bulur ve ikinci farklılıkta durur: O(n² × L); 5.000 kelime için en fazla 25 milyon kelime karşılaştırması.
Algoritma
endWordwordListiçinde değilse 0 döndür.beginWordkelimesini uzunluğu 1 olacak şekilde kuyruğa ekle; listede varsa ziyaret edildi olarak işaretle.- Sıradaki kelimeyi al ve ziyaret edilmemiş her kelimeyle karşılaştır. Aralarında bir harf fark olan her kelime için:
endWordise length + 1 döndür; değilse onu işaretle ve length + 1 uzunluğuyla kuyruğa ekle. - Kuyruk boşalırsa 0 döndür.
from collections import deque
def ladderLength(beginWord, endWord, wordList):
def one_letter_apart(a, b):
differences = 0
for x, y in zip(a, b):
if x != y:
differences += 1
if differences > 1:
return False
return differences == 1
if endWord not in wordList:
return 0
visited = [word == beginWord for word in wordList]
queue = deque([(beginWord, 1)]) # (word, words in the sequence up to it)
while queue:
word, length = queue.popleft()
# Compare against every word to find the neighbours.
for i, candidate in enumerate(wordList):
if not visited[i] and one_letter_apart(word, candidate):
if candidate == endWord:
return length + 1
visited[i] = True
queue.append((candidate, length + 1))
return 0Joker karakter kovalarıyla genişlik öncelikli arama
Sezgi
İki kelime tam olarak bir harf farklıdır; aynı konum gizlendiğinde kelimeler eşit oluyorsa: hot ve hit ikisi de h*t olur. Her kelime için gizlenen her konuma karşılık bir tane olmak üzere L desen oluşturun ve her birini bir kovaya ekleyin. Bir kelimenin komşuları, L liste taraması yerine karma tablosunda L arama yapılarak bulunan, L kovasındaki diğer kelimelerdir.
İlk örnekte, l*ad kovasında load, le*d kovasında ise lend ve lewd bulunur; dolayısıyla 2. seviyede bu üç kelime vardır. Ardından *oad kovası goad kelimesini, go*d kovası da 4. seviyede gold kelimesini verir.
Bir kova tarandığında içindeki her kelimeye ulaşılmış olur; bu yüzden kovayı boşaltın: 26 kelimelik *aa kovası 26 kez değil, bir kez taranır. Maliyet, 10 harfli 5.000 kelime için yaklaşık 500.000 harf adımıyla O(n × L²) zaman ve alan karmaşıklığıdır.
Algoritma
endWord,wordListiçinde değilse 0 döndür.beginWorddahil her sözcüğü,Ldesenlerinin her birinin altına yerleştir.beginWordsözcüğünü ziyaret edildi olarak işaretleyip uzunluğu 1 olacak şekilde kuyruğa ekle.- Bir düzeydeki her sözcük için:
endWordise uzunluğu döndür. Aksi takdirde, her desen kovasındaki ziyaret edilmemiş sözcükleri bir sonraki düzeye taşı, ziyaret edildi olarak işaretle ve kovayı boşalt. - Her düzeyde uzunluğa 1 ekle. Kuyruk boşalırsa 0 döndür.
from collections import defaultdict, deque
def ladderLength(beginWord, endWord, wordList):
if endWord not in wordList:
return 0
size = len(beginWord)
# "h*t" -> every word that matches it: hot, hat, hit... are one letter apart.
buckets = defaultdict(list)
for word in set(wordList) | {beginWord}:
for i in range(size):
buckets[word[:i] + "*" + word[i + 1:]].append(word)
visited = {beginWord}
queue = deque([beginWord])
length = 1 # words in the sequence up to the current level
while queue:
for _ in range(len(queue)): # one level: every word at this distance
word = queue.popleft()
if word == endWord:
return length
for i in range(size):
pattern = word[:i] + "*" + word[i + 1:]
for neighbour in buckets[pattern]:
if neighbour not in visited:
visited.add(neighbour)
queue.append(neighbour)
buckets[pattern] = [] # all of them are visited now: never scan it again
length += 1
return 0
Tuzaklar ve uç durumlar
- Kelimeler yerine değişiklikleri saymak.
leadilegoldarasında 3 değişiklik ve 4 kelime vardır; cevap 4'tür. endWord'ünwordListiçinde olup olmadığını kontrol etmemek. İkinci örnektecog'a ulaşılır;dog'dan bir harf farklı olmasına rağmen cevap 0'dır.- Bir kelimeyi kuyruktan çıkarıldığında ziyaret edilmiş olarak işaretlemek. Böylece
*aakovasındaki bir kelime kuyruğa 25 defaya kadar eklenebilir ve kuyrukn'in çok ötesinde büyür. - Merdiven boyunca özyinelemeli ilerlemek. En kısa merdiven binlerce kelime uzunluğunda olabilir; bu da çağrı yığınını taşırmaya yeter.
Sıkça sorulan sorular3
Word Ladder'ın zaman karmaşıklığı nedir?
Joker karakter kova yaklaşımıyla, uzunluğu L olan n kelime için maliyet O(n × L²) olur: her kelimenin, L harften oluşan L deseni vardır. Her kelime çiftini karşılaştırmanın maliyeti O(n² × L) olur ve derinlik öncelikli aramayla her merdiveni denemek üstel zaman alır.
Bir harf farkı olan kelimeleri nasıl bulursunuz?
Joker kovaları bir yöntemdir. Diğeri, her konumu 26 harfin her biriyle değiştirip sonucu kelimelerin hash kümesinde aramaktır: kelime başına 26 × L kez L harfli arama, toplamda O(n × 26 × L²). Her iki yöntem de listeyi baştan sona taramaktan daha iyidir.
Çift yönlü BFS, Word Ladder'ı daha hızlı hâle getirebilir mi?
Evet. Aynı anda her iki uçtan arama yapın, her zaman küçük olan tarafı bir seviye büyütün ve iki taraf buluştuğunda durun. Her kelime için yaklaşık b komşu ve d değişiklik olduğunda, tek bir arama yaklaşık b^d kelimeye dokunabilir; ortada buluşan iki arama ise yaklaşık 2 × b^(d/2) kelimeye dokunur.
Benzer problemler
Aynı fikirleri kullanan problemler. İki üçünü çözmek bir kalıbı kalıcı hale getirir.
def ladderLength(beginWord, endWord, wordList):
# Kodu buraya yazınTest #1
Test #2
Test #3
Argümanlar
beginWord = "lead" endWord = "gold" wordList = ["load", "goad", "gold", "lend", "lewd", "bold"]
Beklenen Çıktı
4