0Pricing
Coding Interview Prep · Ders

Düzenleme Uzaklığı (Levenshtein)

Ekleme/silme/değiştirme işlemleri için düzenleme uzaklığı bağıntısını çıkarın ve farklı uzunluklardaki dize çiftleri için DP tablosunu doldurun.

Düzenleme Uzaklığı (Levenshtein), CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Coding Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Coding Interview Prep kursu toplamda 4 dersten oluşur.

Düzenleme Mesafesi Problemi

Düzenleme Mesafesi (Levenshtein mesafesi, LeetCode 72) şu soruyu sorar: Bir dizeyi başka bir dizeye dönüştürmek için gereken minimum ekleme, silme veya değiştirme işlemi sayısı nedir? Örneğin, 'horse' dizesini 'ros' dizesine dönüştürmek için 'h'→'r' değişikliği (horse→rorse), 'r' silme (rorse→rose) ve 'e' silme (rose→ros) yapılır; toplam 3 işlem gerekir. Düzenleme mesafesi, yazım denetleyicilerinde, DNA hizalamasında ve yaklaşık eşleştirmede temel bir kavramdır.

# Allowed operations:
# Insert: 'abc' → 'abXc' (insert X)
# Delete: 'abc' → 'ac' (delete b)
# Replace: 'abc' → 'aXc' (replace b with X)

# horse → ros: 3 operations
# 1. horse → rorse (replace h with r)
# 2. rorse → rose  (delete r at index 1)
# 3. rose  → ros   (delete e)
print('Edit distance horse→ros: 3')
print('Edit distance intention→execution: 5')

DP Durumu ve Bağıntı

dp[i][j] değerini word1[:i] ile word2[:j] arasındaki minimum düzenleme mesafesi olarak tanımlayın. Eğer word1[i-1] == word2[j-1] ise işlem gerekmez: dp[i][j] = dp[i-1][j-1]. Aksi hâlde üç işlemin minimumunu alın: ekleme dp[i][j-1] + 1, silme dp[i-1][j] + 1, değiştirme dp[i-1][j-1] + 1. Temel durumlar: dp[i][0] = i (word1'in tamamını silme) ve dp[0][j] = j (word2'nin tamamını ekleme).

def edit_distance(word1, word2):
    m, n = len(word1), len(word2)
    dp = [[0]*(n+1) for _ in range(m+1)]
    # Base cases
    for i in range(m+1): dp[i][0] = i  # delete all of word1
    for j in range(n+1): dp[0][j] = j  # insert all of word2
    for i in range(1, m+1):
        for j in range(1, n+1):
            if word1[i-1] == word2[j-1]:
                dp[i][j] = dp[i-1][j-1]  # no cost
            else:
                dp[i][j] = 1 + min(
                    dp[i][j-1],    # insert
                    dp[i-1][j],    # delete
                    dp[i-1][j-1]   # replace
                )
    return dp[m][n]

print(edit_distance('horse', 'ros'))          # 3
print(edit_distance('intention', 'execution')) # 5

Üç İşlemi Anlamak

Üç işlem, DP tablosundaki hareketlere doğrudan karşılık gelir: Değiştirme dp[i-1][j-1]+1 — her iki karakteri eşleştirdik ancak bir maliyet ödedik. word1'den silme dp[i-1][j]+1 — word1'den bir karakteri kaldırın (tabloda yukarı hareket edin). word1'e ekleme dp[i][j-1]+1 — word2'deki karakterle eşleşmesi için bir karakter ekleyin (sola hareket edin). Üç değerin minimumu en iyi düzenleme yolunu verir.

# Visualise the DP table for 'cat' → 'cut'
# dp[i][j] = min edits for word1[:i] vs word2[:j]

word1, word2 = 'cat', 'cut'
m, n = len(word1), len(word2)
dp = [[0]*(n+1) for _ in range(m+1)]
for i in range(m+1): dp[i][0] = i
for j in range(n+1): dp[0][j] = j
for i in range(1, m+1):
    for j in range(1, n+1):
        if word1[i-1]==word2[j-1]: dp[i][j]=dp[i-1][j-1]
        else: dp[i][j]=1+min(dp[i][j-1],dp[i-1][j],dp[i-1][j-1])
print('  ', ' '.join(' '+word2))
for i, row in enumerate(dp):
    print((' ' if i==0 else word1[i-1]), row)

O(n) Bellek Kullanımına Optimizasyon

Düzenleme mesafesi yalnızca geçerli ve önceki satıra ihtiyaç duyar. n+1 boyutunda bir 1B dizi kullanın ve her hücre güncellenmeden önce diagonal değerini (dp[i-1][j-1]) ayrı olarak takip edin. Soldan sağa ilerleyin: temp = dp[j] (eski değer = dp[i-1][j]); ardından dp[j] (silme), dp[j-1] (ekleme) ve diagonal (değiştirme) değerlerini kullanarak dp[j] değerini güncelleyin.

def edit_distance_1d(word1, word2):
    m, n = len(word1), len(word2)
    dp = list(range(n + 1))  # initial row: 0,1,2,...,n
    for i in range(1, m + 1):
        diag = dp[0]       # dp[i-1][0]
        dp[0] = i          # dp[i][0] = i
        for j in range(1, n + 1):
            temp = dp[j]   # dp[i-1][j] before overwrite
            if word1[i-1] == word2[j-1]:
                dp[j] = diag
            else:
                dp[j] = 1 + min(dp[j],     # delete
                                dp[j-1],   # insert
                                diag)      # replace
            diag = temp
    return dp[n]

print(edit_distance_1d('horse', 'ros'))          # 3
print(edit_distance_1d('intention', 'execution')) # 5

Düzenleme İşlemlerinin Yeniden Oluşturulması

Gerçek düzenleme dizisini yeniden oluşturmak için (m, n) konumundan başlayarak DP tablosunda geriye doğru ilerleyin. Her hücrede şu işlemleri yapın: word1[i-1] == word2[j-1] ise çapraz ilerleyin (işlem yok). Aksi hâlde, üç komşudan hangisinin minimum değeri verdiğini bulun ve karşılık gelen işlemi kaydedin. Bu işlem betiğini tersten oluşturur; son yanıtı elde etmek için betiği ters çevirin.

def edit_ops(word1, word2):
    m, n = len(word1), len(word2)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0]=i
    for j in range(n+1): dp[0][j]=j
    for i in range(1,m+1):
        for j in range(1,n+1):
            if word1[i-1]==word2[j-1]: dp[i][j]=dp[i-1][j-1]
            else: dp[i][j]=1+min(dp[i][j-1],dp[i-1][j],dp[i-1][j-1])
    ops, i, j = [], m, n
    while i>0 or j>0:
        if i>0 and j>0 and word1[i-1]==word2[j-1]:
            i-=1; j-=1
        elif j>0 and (i==0 or dp[i][j-1]<=dp[i-1][j] and dp[i][j-1]<=dp[i-1][j-1]):
            ops.append(f'Insert {word2[j-1]} at pos {i}'); j-=1
        elif i>0 and (j==0 or dp[i-1][j]<=dp[i][j-1] and dp[i-1][j]<=dp[i-1][j-1]):
            ops.append(f'Delete {word1[i-1]} at pos {i-1}'); i-=1
        else:
            ops.append(f'Replace {word1[i-1]} with {word2[j-1]}'); i-=1; j-=1
    return list(reversed(ops))

for op in edit_ops('horse', 'ros'): print(op)

Tek Düzenleme Mesafesi Kontrolü

Daha basit bir mülakat problemi şudur: İki dize birbirinden tam olarak bir düzenleme uzaklıkta mı? Bu işlem DP olmadan O(n) zamanda yapılabilir. İki dizeyi eş zamanlı olarak tarayın. Bir uyuşmazlıkta üç işlemin tümünü deneyin (s1'deki bir karakteri atlayın, s2'deki bir karakteri atlayın, her ikisini de atlayın) ve geriye kalan bölümlerin aynı olup olmadığını denetleyin. İki uyuşmazlık oluşursa False döndürün. Bu açgözlü yaklaşım, yalnızca mesafenin ≤ 1 olup olmadığını bilmeniz gerektiğinde tam O(mn) DP kullanımını önler.

def is_one_edit_distance(s, t):
    m, n = len(s), len(t)
    if abs(m - n) > 1: return False
    if m > n: return is_one_edit_distance(t, s)  # ensure m <= n
    for i in range(m):
        if s[i] != t[i]:
            if m == n:
                return s[i+1:] == t[i+1:]   # replace
            else:
                return s[i:] == t[i+1:]     # insert into s (delete from t)
    return m + 1 == n  # all matched, lengths differ by 1

print(is_one_edit_distance('ab', 'acb'))   # True (insert c)
print(is_one_edit_distance('ab', 'ab'))    # False (zero edits)
print(is_one_edit_distance('ab', 'abc'))   # True (append c)
print(is_one_edit_distance('ab', 'xyz'))   # False

Düzenleme Mesafesi ve LCS Karşılaştırması

Düzenleme mesafesi (üç işlemin tümüyle) ve LCS, dize benzerliğine tamamlayıcı bakış açıları sunar. Düzenleme mesafesi farkı, LCS ise benzerliği sayar. Yalnızca ekleme ve silmeye izin verildiğinde (değiştirme yok), düzenleme mesafesi = m + n - 2×LCS olur. Değiştirmeye izin verildiğinde DP biraz farklıdır: eşleşmede çapraz değer dp[i-1][j-1]'e (ücretsiz), değiştirmede ise dp[i-1][j-1]+1'e katkıda bulunur. Her iki algoritma da O(mn) zamanda çalışır.

def lcs_len(s1, s2):
    m, n = len(s1), len(s2)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1,m+1):
        for j in range(1,n+1):
            if s1[i-1]==s2[j-1]: dp[i][j]=dp[i-1][j-1]+1
            else: dp[i][j]=max(dp[i-1][j],dp[i][j-1])
    return dp[m][n]

def edit_insert_delete_only(s1, s2):
    return len(s1) + len(s2) - 2 * lcs_len(s1, s2)

print(edit_insert_delete_only('sea', 'eat'))  # 2
print(edit_distance('sea', 'eat'))            # 2 (same here: replace not needed)

Yaklaşık Dize Eşleştirme

Düzenleme mesafesi, gerçek dünyadaki yaklaşık eşleştirme işlemlerini mümkün kılar. Bir yazım denetleyici, yazılan kelimenin düzenleme mesafesi 1 veya 2 olan düzeltmelerini önerir. Büyük ölçekteki zorluk, O(mn × dict_size) karşılaştırmadan kaçınmaktır. Çözümler arasında BK ağaçları (düzenleme mesafesi için bir metrik ağaç), n-gram dizinleme ve Bitap gibi yaklaşık dize eşleştirme algoritmaları bulunur. Temel DP'yi anlamak, bu üst düzey araçların verimliliği hakkında akıl yürütmenize yardımcı olur.

def spell_suggest(typed, dictionary, max_dist=2):
    '''Return words in dictionary within max_dist edits of typed.'''
    suggestions = []
    for word in dictionary:
        if abs(len(typed) - len(word)) <= max_dist:
            if edit_distance(typed, word) <= max_dist:
                suggestions.append(word)
    return suggestions

def edit_distance(w1, w2):
    dp = list(range(len(w2)+1))
    for i,c1 in enumerate(w1,1):
        prev = i
        for j,c2 in enumerate(w2,1):
            temp = dp[j]
            dp[j] = prev if c1==c2 else 1+min(dp[j],prev,dp[j-1])
            prev = temp
    return dp[len(w2)]

dictionary = ['horse', 'worse', 'house', 'morse', 'nurse']
print(spell_suggest('harse', dictionary))  # horse, worse, house, morse

Ağırlıklı Düzenleme Mesafesi

Bazı uygulamalarda farklı işlemlerin farklı maliyetleri vardır. Örneğin, bitişik karakterlerin yer değiştirmesi (yaygın bir yazım hatası) tam bir değiştirmeden daha düşük maliyetli olabilir. Damerau-Levenshtein mesafesi, yer değiştirmeyi dördüncü bir işlem olarak ekler. DP şu şekilde genişletilir: word1[i-1]==word2[j-2] ve word1[i-2]==word2[j-1] olduğunda dp[i-2][j-2]+1 değerini de denetleyin. Bu yaklaşım, klavye yazım hatalarını daha doğru modeller.

def damerau_levenshtein(s, t):
    m, n = len(s), len(t)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0]=i
    for j in range(n+1): dp[0][j]=j
    for i in range(1,m+1):
        for j in range(1,n+1):
            cost = 0 if s[i-1]==t[j-1] else 1
            dp[i][j] = min(
                dp[i-1][j]+1,     # delete
                dp[i][j-1]+1,     # insert
                dp[i-1][j-1]+cost # replace
            )
            # Transposition
            if i>1 and j>1 and s[i-1]==t[j-2] and s[i-2]==t[j-1]:
                dp[i][j] = min(dp[i][j], dp[i-2][j-2]+1)
    return dp[m][n]

print(damerau_levenshtein('CA', 'ABC'))   # 2
print(damerau_levenshtein('ab', 'ba'))    # 1 (transposition)

DNA Dizisi Hizalaması

Biyoinformatik, DNA dizisi hizalaması için düzenleme mesafesinin farklı türevlerini kullanır. Needleman-Wunsch algoritması, LCS ve düzenleme mesafesiyle yakından ilişkili bir global hizalama DP'sidir; eşleşme +1, uyuşmazlık -1, boşluk (ekleme/silme) ise bir ceza verir. Smith-Waterman türevi yerel hizalama gerçekleştirir (en iyi eşleşen alt dizeyi bulur). Her ikisi de aynı tablo doldurma yapısına sahip O(mn) DP algoritmalarıdır.

def needleman_wunsch(seq1, seq2, match=1, mismatch=-1, gap=-1):
    m, n = len(seq1), len(seq2)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0] = i * gap
    for j in range(n+1): dp[0][j] = j * gap
    for i in range(1,m+1):
        for j in range(1,n+1):
            score = match if seq1[i-1]==seq2[j-1] else mismatch
            dp[i][j] = max(
                dp[i-1][j-1] + score,  # align
                dp[i-1][j] + gap,      # gap in seq2
                dp[i][j-1] + gap       # gap in seq1
            )
    return dp[m][n]

print(needleman_wunsch('GATTACA', 'GCATGCU'))  # alignment score

Düzenleme Mesafesi İçin Mülakat Yaklaşımı

Bir mülakatta düzenleme mesafesi sorulduğunda: (1) İzin verilen işlemleri (ekleme/silme/değiştirme) doğrulayın. (2) DP durumunu açıkça tanımlayın. (3) Üç durumu ve bağıntıyı açıkça yazın. (4) Temel durumları belirtin: dp[i][0]=i ve dp[0][j]=j. (5) O(n) bellek optimizasyonundan bahsedin. (6) Zaman kalırsa doğrulamak için 'cat'→'cut' (1 değiştirme) gibi küçük bir örneği adım adım inceleyin. O(mn) zaman ve O(mn) → O(n) bellek, standart karmaşıklık sınırlarıdır.

# Clean interview solution
def min_distance(word1, word2):
    m, n = len(word1), len(word2)
    # O(n) space with rolling row
    dp = list(range(n + 1))
    for i in range(1, m + 1):
        diag = dp[0]   # dp[i-1][0]
        dp[0] = i
        for j in range(1, n + 1):
            temp = dp[j]
            if word1[i-1] == word2[j-1]:
                dp[j] = diag
            else:
                dp[j] = 1 + min(dp[j], dp[j-1], diag)
            diag = temp
    return dp[n]

# Time: O(mn), Space: O(n)
print(min_distance('horse', 'ros'))          # 3
print(min_distance('intention', 'execution')) # 5
print(min_distance('', 'abc'))               # 3
print(min_distance('abc', ''))               # 3

Hızlı Kontrol

Bu dersteki Veri Yapıları & Algoritmalar — Kodlama Mülakatı Hazırlığı kavramlarını ne kadar anladığınızı test edin.

Ders Özeti

Bu derste şunları öğrendiniz: düzenleme mesafesi dp[i][j] = min(dp[i][j-1]+1, dp[i-1][j]+1, dp[i-1][j-1]+cost); eşleşmede cost=0, aksi hâlde 1, dp[i][0]=i ve dp[0][j]=j temel durumları boş bir dizeye dönüştürmeyi veya boş bir dizeden dönüştürmeyi temsil eder ve O(n) bellek optimizasyonu, diyagonal değişkeni içeren kayan 1B dizi kullanır. Sırada, 2B DP tablolarının bellek kullanımını O(mn)'den O(min(m,n)) değerine düşürmek için aynı kayan dizi tekniğini uygulayacağız.

Sıkça Sorulan Sorular

“Düzenleme Uzaklığı (Levenshtein)” dersi ücretsiz mi?

Evet — “Düzenleme Uzaklığı (Levenshtein)” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Coding Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Coding Interview Prep kursu toplamda 4 dersten oluşur.

“Düzenleme Uzaklığı (Levenshtein)” dersinde ne öğreneceğim?

Ekleme/silme/değiştirme işlemleri için düzenleme uzaklığı bağıntısını çıkarın ve farklı uzunluklardaki dize çiftleri için DP tablosunu doldurun. Coding Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Coding Interview Prep öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Coding Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.

“Düzenleme Uzaklığı (Levenshtein)” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Coding Interview Prep dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Coding Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Izgaralarda Benzersiz Yollar ve Minimum Yol Toplamı
  2. En Uzun Ortak Alt Dizi
  3. Düzenleme Uzaklığı (Levenshtein)
  4. İki Boyutlu DP için Alan Optimizasyonu
← Coding Interview Prep Sayfasına Dön