DSA Interview Prep · पाठ

Edit Distance (Levenshtein)

insert/delete/replace संचालन के लिए edit-distance पुनरावृत्ति निकालिए और अलग-अलग लंबाई वाली स्ट्रिंग-युग्मों की DP तालिका भरिए।

पाठ 3, कुल 4 में से13 चरण

Edit Distance (Levenshtein), CoddyKit पर DSA Interview Prep का एक निःशुल्क पाठ है। यह 4 में से 3वाँ पाठ है। इस अध्ययन पथ के 3 तक कोई भी पाठ पूरा पढ़ना निःशुल्क है — इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ व्यावहारिक अभ्यास भी उपलब्ध कराता है। यह DSA Interview Prep सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। DSA Interview Prep पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

संपादन दूरी की समस्या

संपादन दूरी (लेवेनश्टीन दूरी, LeetCode 72) में पूछा जाता है: एक स्ट्रिंग को दूसरी स्ट्रिंग में बदलने के लिए आवश्यक जोड़ने, हटाने या प्रतिस्थापित करने की क्रियाओं की न्यूनतम संख्या क्या है? उदाहरण के लिए, 'horse' को 'ros' में बदलने के लिए 'h'→'r' को प्रतिस्थापित करें (horse→rorse), 'r' हटाएँ (rorse→rose), और 'e' हटाएँ (rose→ros)—कुल 3 क्रियाएँ। संपादन दूरी वर्तनी-जाँचकों, DNA संरेखण और लगभग-मिलान की आधारभूत अवधारणा है।

# 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 स्थिति और पुनरावृत्ति

dp[i][j] को word1[:i] और word2[:j] के बीच की न्यूनतम संपादन दूरी के रूप में परिभाषित करें। यदि word1[i-1] == word2[j-1], तो किसी क्रिया की आवश्यकता नहीं है: dp[i][j] = dp[i-1][j-1]। अन्यथा, तीन क्रियाओं में से न्यूनतम लें: जोड़ना dp[i][j-1] + 1, हटाना dp[i-1][j] + 1, प्रतिस्थापित करना dp[i-1][j-1] + 1। आधार स्थितियाँ: dp[i][0] = i (word1 के सभी वर्ण हटाना) और dp[0][j] = j (word2 के सभी वर्ण जोड़ना)।

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

तीनों क्रियाओं को समझना

तीनों क्रियाएँ DP तालिका में होने वाली गतियों से सीधे जुड़ी हैं: प्रतिस्थापन dp[i-1][j-1]+1 — हमने दोनों वर्णों का मिलान किया, लेकिन एक लागत चुकाई। word1 से हटाना dp[i-1][j]+1 — word1 से एक वर्ण हटाएँ (तालिका में ऊपर जाएँ)। word1 में जोड़ना dp[i][j-1]+1 — word2 से मिलान करने के लिए एक वर्ण जोड़ें (बाएँ जाएँ)। तीनों में से न्यूनतम मान सर्वोत्तम संपादन पथ देता है।

# 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) तक स्पेस का अनुकूलन

संपादन दूरी के लिए केवल वर्तमान पंक्ति और पिछली पंक्ति आवश्यक होती है। n+1 आकार का 1D ऐरे उपयोग करें और प्रत्येक कोशिका को अद्यतन करने से पहले diagonal मान (dp[i-1][j-1]) को अलग से ट्रैक करें। बाएँ से दाएँ प्रक्रिया करें: temp = dp[j] (पुराना मान = dp[i-1][j]), फिर dp[j] (हटाना), dp[j-1] (जोड़ना) और diagonal (प्रतिस्थापन) का उपयोग करके dp[j] को अद्यतन करें।

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

संपादन क्रियाओं का पुनर्निर्माण

संपादनों के वास्तविक क्रम का पुनर्निर्माण करने के लिए (m, n) से DP तालिका में पीछे की ओर जाएँ। प्रत्येक कोशिका पर: यदि word1[i-1] == word2[j-1], तो तिरछे जाएँ (कोई क्रिया नहीं)। अन्यथा, देखें कि तीन पड़ोसी कोशिकाओं में से किसने न्यूनतम मान दिया और संबंधित क्रिया दर्ज करें। इससे संपादन क्रम उलटे क्रम में प्राप्त होगा; अंतिम उत्तर के लिए उसे उलट दें।

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)

एक संपादन दूरी की जाँच

साक्षात्कार में आने वाली एक सरल समस्या: क्या दो स्ट्रिंगों के बीच ठीक एक संपादन का अंतर है? इसे DP के बिना O(n) में हल किया जा सकता है। दोनों स्ट्रिंगों पर एक साथ आगे बढ़ें। असमानता मिलने पर तीनों क्रियाएँ आज़माएँ (s1 में एक वर्ण छोड़ें, s2 में एक वर्ण छोड़ें, दोनों में एक-एक वर्ण छोड़ें) और जाँचें कि शेष भाग समान हैं। यदि दो असमानताएँ मिलें, तो False लौटाएँ। जब आपको केवल यह जानना हो कि दूरी ≤ 1 है, तब यह लालची तरीका पूर्ण O(mn) DP से बचाता है।

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

संपादन दूरी और LCS की तुलना

सभी तीन क्रियाओं वाली संपादन दूरी और LCS, स्ट्रिंग की समानता को देखने के दो पूरक दृष्टिकोण हैं। संपादन दूरी अंतर गिनती है; LCS समानता गिनता है। जब केवल जोड़ने और हटाने की अनुमति हो (प्रतिस्थापन नहीं), तो संपादन दूरी = m + n - 2×LCS। जब प्रतिस्थापन की अनुमति हो, तो DP थोड़ा अलग होता है: मिलान पर तिरछा मान dp[i-1][j-1] (निःशुल्क) और प्रतिस्थापन पर dp[i-1][j-1]+1 देता है। दोनों एल्गोरिदम O(mn) समय में चलते हैं।

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)

अनुमानित स्ट्रिंग मिलान

संपादन दूरी वास्तविक दुनिया के अनुमानित मिलान को सक्षम बनाती है। वर्तनी-जाँचक टाइप किए गए शब्द से संपादन दूरी 1 या 2 के भीतर के सुधार सुझाता है। बड़े पैमाने पर चुनौती O(mn × dict_size) तुलनाओं से बचना है। समाधानों में BK-वृक्ष (संपादन दूरी के लिए एक मेट्रिक वृक्ष), एन-ग्राम अनुक्रमण और बिटैप जैसे लगभग-स्ट्रिंग-मिलान एल्गोरिदम शामिल हैं। अंतर्निहित DP को समझने से आपको इन उच्च-स्तरीय उपकरणों की दक्षता पर विचार करने में सहायता मिलती है।

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

भारित संपादन दूरी

कुछ अनुप्रयोगों में अलग-अलग क्रियाओं की अलग-अलग लागत होती है। उदाहरण के लिए, पास-पास के वर्णों को स्थानांतरित करना (एक सामान्य टाइपिंग गलती) पूर्ण प्रतिस्थापन से कम महँगा हो सकता है। डैमराउ-लेवेनश्टीन दूरी चौथी क्रिया के रूप में स्थानांतरण जोड़ती है। DP का विस्तार इस प्रकार होता है: जब word1[i-1]==word2[j-2] और word1[i-2]==word2[j-1] हो, तब dp[i-2][j-2]+1 भी जाँचें। यह कुंजीपटल की टाइपिंग गलतियों का अधिक सटीक मॉडल बनाता है।

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 अनुक्रम संरेखण

जैव-सूचनाविज्ञान में DNA अनुक्रम संरेखण के लिए संपादन दूरी के विभिन्न रूपों का उपयोग किया जाता है। नीडलमैन-वुन्श एल्गोरिदम LCS और संपादन दूरी से संबंधित वैश्विक संरेखण का DP है, जिसमें मिलान पर +1, असमानता पर -1 और रिक्ति (जोड़ना/हटाना) पर दंड मिलता है। स्मिथ-वॉटरमैन रूप स्थानीय संरेखण करता है (सबसे अच्छे मेल वाली उपस्ट्रिंग ढूँढ़ता है)। दोनों O(mn) DP एल्गोरिदम हैं और इनमें तालिका भरने की एक ही संरचना होती है।

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

संपादन दूरी के लिए साक्षात्कार दृष्टिकोण

साक्षात्कार में संपादन दूरी पूछे जाने पर: (1) अनुमत क्रियाओं की पुष्टि करें (जोड़ना/हटाना/प्रतिस्थापित करना)। (2) DP स्थिति को स्पष्ट रूप से परिभाषित करें। (3) तीनों स्थितियाँ और पुनरावृत्ति स्पष्ट रूप से लिखें। (4) आधार स्थितियाँ बताएँ: dp[i][0]=i और dp[0][j]=j। (5) O(n) स्पेस अनुकूलन का उल्लेख करें। (6) यदि समय हो, तो सत्यापन के लिए 'cat'→'cut' जैसे छोटे उदाहरण को देखें (1 प्रतिस्थापन)। O(mn) समय और O(mn) → O(n) स्पेस मानक जटिलता सीमाएँ हैं।

# 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

त्वरित जाँच

इस पाठ में सिखाई गई डेटा संरचनाएँ & एल्गोरिदम — कोडिंग साक्षात्कार तैयारी की अवधारणाओं की अपनी समझ जाँचें।

पाठ का पुनरावलोकन

इस पाठ में आपने सीखा: संपादन दूरी dp[i][j] = min(dp[i][j-1]+1, dp[i-1][j]+1, dp[i-1][j-1]+cost), जहाँ मिलान पर cost=0 और अन्यथा 1, आधार स्थितियाँ dp[i][0]=i और dp[0][j]=j खाली स्ट्रिंग में या उससे रूपांतरण को दर्शाती हैं, और O(n) स्पेस अनुकूलन में diagonal चर वाला रोलिंग 1D ऐरे उपयोग होता है। आगे हम इसी रोलिंग-ऐरे युक्ति से 2D DP तालिकाओं के स्पेस को O(mn) से घटाकर O(min(m,n)) करेंगे।

शुरुआत निःशुल्क

एआई शिक्षक के साथ Python सीखें — निःशुल्क

अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।

पाठ्यक्रम
30
पाठ
120

अक्सर पूछे जाने वाले प्रश्न

क्या “Edit Distance (Levenshtein)” पाठ निःशुल्क है?

हाँ — DSA Interview Prep अध्ययन पथ के 3 तक कोई भी पाठ, जिसमें “Edit Distance (Levenshtein)” भी शामिल है, यहाँ वेब पर पूरा पढ़ना निःशुल्क है। इसके बाद CoddyKit PRO हर पाठ अनलॉक करता है, साथ ही अंतर्निर्मित कोड संपादक और चौबीसों घंटे एआई शिक्षक के साथ इंटरैक्टिव अभ्यास भी उपलब्ध कराता है। DSA Interview Prep पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“Edit Distance (Levenshtein)” में मैं क्या सीखूँगा?

insert/delete/replace संचालन के लिए edit-distance पुनरावृत्ति निकालिए और अलग-अलग लंबाई वाली स्ट्रिंग-युग्मों की DP तालिका भरिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ DSA Interview Prep का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या DSA Interview Prep शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर DSA Interview Prep शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 3वाँ पाठ है।

“Edit Distance (Levenshtein)” पाठ पूरा करने में कितना समय लगता है?

CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।

क्या मैं इस DSA Interview Prep पाठ में कोड लिख और चला सकता हूँ?

हाँ। हर DSA Interview Prep पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।

इस पाठ्यक्रम के सभी पाठ

  1. ग्रिड पर विशिष्ट पथ और न्यूनतम पथ योग
  2. Longest Common Subsequence
  3. Edit Distance (Levenshtein)
  4. 2D DP के लिए स्थान अनुकूलन
← DSA Interview Prep पर वापस जाएँ