कोडिंग साक्षात्कार की तैयारी · पाठ

2D DP के लिए स्थान अनुकूलन

DP तालिका की केवल वर्तमान और पिछली पंक्तियाँ रखकर LCS और edit-distance का स्थान O(mn) से O(min(m,n)) तक घटाइए।

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

2D DP के लिए स्थान अनुकूलन, CoddyKit पर कोडिंग साक्षात्कार की तैयारी का एक निःशुल्क पाठ है। यह 4 में से 4वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह कोडिंग साक्षात्कार की तैयारी सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। कोडिंग साक्षात्कार की तैयारी पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

2D DP में स्पेस क्यों महत्वपूर्ण है

1000 लंबाई वाली स्ट्रिंगों के लिए 2D DP तालिका में 1000×1000 = 1,000,000 कोशिकाएँ चाहिए—64-बिट पूर्णांकों के लिए लगभग 8 MB। लंबी अनुक्रमों (DNA संरेखण, बड़े पाठ का diff) के लिए यह अव्यावहारिक हो जाता है। मुख्य अवलोकन यह है कि अधिकांश 2D DP पुनरावृत्तियाँ केवल वर्तमान और पिछली पंक्ति को देखती हैं, इसलिए पूरी तालिका को एक या दो 1D ऐरे में संकुचित किया जा सकता है। यही 2D DP स्पेस अनुकूलन का मूल है।

# Full 2D DP: O(mn) space
# LCS for 1000-char strings
m, n = 1000, 1000
dp_2d_size = m * n * 8  # bytes (64-bit ints)
print(f'2D table: {dp_2d_size:,} bytes = {dp_2d_size//1024} KB')

# 1D rolling array: O(n) space
dp_1d_size = n * 8
print(f'1D array: {dp_1d_size:,} bytes = {dp_1d_size} bytes')
print(f'Space saving: {dp_2d_size // dp_1d_size}x')

रोलिंग ऐरे प्रतिरूप

रोलिंग ऐरे प्रतिरूप पूरी 2D तालिका के स्थान पर पिछली पंक्ति को दर्शाने वाला 1D ऐरे उपयोग करता है। पंक्ति i की गणना करते समय, आप प्रत्येक कोशिका j को वर्तमान मान dp[j] (जिसमें अभी भी पिछली पंक्ति का dp[i-1][j] मौजूद है) और अभी-अभी अद्यतन किए गए dp[j-1] (जो dp[i][j-1] है) का उपयोग करके अद्यतन करते हैं। diagonal चर अधिलेखित होने से पहले dp[i-1][j-1] को सुरक्षित रखता है। यह प्रतिरूप LCS, संपादन दूरी और अधिकांश 2D DP समस्याओं पर लागू होता है।

# Rolling array template for 2D DP
# Before update: dp[j] holds dp[i-1][j] (previous row)
# After update: dp[j] holds dp[i][j] (current row)

def rolling_array_template(grid):
    m, n = len(grid), len(grid[0])
    dp = [0] * (n + 1)  # represents one row
    for i in range(1, m + 1):
        diag = 0  # stores dp[i-1][j-1] before overwrite
        for j in range(1, n + 1):
            temp = dp[j]  # save dp[i-1][j] before overwriting
            # compute dp[i][j] using dp[j] (above) and dp[j-1] (left) and diag
            dp[j] = diag + dp[j] + dp[j-1]  # placeholder logic
            diag = temp
    return dp[n]

O(min m,n) स्पेस के साथ LCS

LCS के लिए सुनिश्चित करें कि text1 छोटी स्ट्रिंग हो (ताकि n छोटा रहे)। n+1 आकार का 1D ऐरे आवंटित करें। पंक्ति-दर-पंक्ति प्रक्रिया करें। प्रत्येक कोशिका पर: temp = dp[j] सहेजें (यह dp[i-1][j] है)। फिर: यदि वर्ण मेल खाते हैं, तो dp[j] = diag + 1; अन्यथा dp[j] = max(dp[j], dp[j-1])। अंत में diag = temp सेट करें। सभी पंक्तियों के बाद dp[n] में LCS की लंबाई होगी।

def lcs_space_opt(text1, text2):
    # Ensure text2 is the shorter one
    if len(text1) < len(text2):
        text1, text2 = text2, text1
    m, n = len(text1), len(text2)
    dp = [0] * (n + 1)
    for i in range(1, m + 1):
        diag = 0
        for j in range(1, n + 1):
            temp = dp[j]  # dp[i-1][j]
            if text1[i-1] == text2[j-1]:
                dp[j] = diag + 1
            else:
                dp[j] = max(dp[j], dp[j-1])
            diag = temp
    return dp[n]

print(lcs_space_opt('ABCBDAB', 'BDCABA'))  # 4
print(lcs_space_opt('AGGTAB', 'GXTXAYB')) # 4

O(n) स्पेस के साथ संपादन दूरी

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

def edit_dist_opt(s, t):
    m, n = len(s), len(t)
    dp = list(range(n + 1))   # row 0: dp[0][j] = j
    for i in range(1, m + 1):
        diag = dp[0]           # dp[i-1][0] before dp[0] update
        dp[0] = i              # dp[i][0] = i
        for j in range(1, n + 1):
            temp = dp[j]       # dp[i-1][j]
            cost = 0 if s[i-1] == t[j-1] else 1
            dp[j] = min(
                dp[j-1] + 1,  # insert
                dp[j] + 1,    # delete
                diag + cost   # replace or match
            )
            diag = temp
    return dp[n]

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

O(n) स्थान वाला न्यूनतम पथ योग

ग्रिड पर न्यूनतम पथ योग के लिए, 1D गतिशील सारणी की शुरुआत पहली पंक्ति के उपसर्ग योगों से होती है (पहली पंक्ति के प्रत्येक खाने तक पहुँचने का केवल एक ही तरीका होता है)। इसके बाद की प्रत्येक पंक्ति के लिए बाएँ से दाएँ अद्यतन करें: अद्यतन से पहले dp[j] ऊपर वाली पंक्ति का मान (dp[i-1][j]) होता है, और अभी-अभी अद्यतन किया गया dp[j-1] बाईं ओर का मान होता है। यहाँ विकर्ण की आवश्यकता नहीं है, क्योंकि न्यूनतम पथ योग में विकर्ण खाने की आवश्यकता नहीं होती।

def min_path_sum_opt(grid):
    m, n = len(grid), len(grid[0])
    dp = [float('inf')] * n
    dp[0] = 0
    for i in range(m):
        # Update first column (only from above)
        dp[0] += grid[i][0]
        for j in range(1, n):
            # min of above (dp[j] = old) and left (dp[j-1] = updated)
            dp[j] = grid[i][j] + min(dp[j], dp[j-1])
    return dp[n-1]

grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum_opt(grid))  # 7

जब विकर्ण तक पहुँचना आवश्यक हो

सभी 2D DP समस्याओं को सरल गतिशील सारणी से संक्षिप्त नहीं किया जा सकता, क्योंकि कुछ में विकर्ण तत्व dp[i-1][j-1] की आवश्यकता होती है, जबकि dp[j] पहले ही अधिलेखित हो चुका होता है। इसका समाधान हमेशा एक जैसा है: अद्यतन करने से पहले temp = dp[j] सहेजें और अगले खाने की गणना के लिए इसे diag के रूप में उपयोग करें। यह एक-खाने का अग्रिम संदर्भ उन सभी तीन-दिशीय पुनरावृत्ति संबंधों (LCS, संपादन दूरी) को सरलता से संभालता है।

# Recap: the diagonal save pattern
# Without it: dp[j-1] updated (left) and dp[j] about to be overwritten
# With it:

def show_diagonal_pattern(s1, s2):
    n = len(s2)
    dp = [0] * (n + 1)
    for ch1 in s1:
        diag = 0  # was dp[i-1][0] = 0 for LCS
        for j, ch2 in enumerate(s2, 1):
            temp = dp[j]  # SAVE before overwrite
            if ch1 == ch2:
                dp[j] = diag + 1  # use saved diagonal
            else:
                dp[j] = max(dp[j], dp[j-1])
            diag = temp  # advance diagonal
    return dp[n]

print(show_diagonal_pattern('ABCBDAB', 'BDCABA'))  # 4

2D नैपसैक का स्थान अनुकूलन

0/1 नैपसैक समस्या को भी स्थान अनुकूलन से लाभ मिलता है। पूरी 2D सारणी के आयाम (n_items+1) × (capacity+1) होते हैं। गतिशील सारणी इसे O(capacity) तक घटा देती है। LCS और संपादन-दूरी से महत्वपूर्ण अंतर यह है कि क्षमता वाले आयाम पर उल्टी दिशा में पुनरावृत्ति करें (बड़े मान से छोटे मान की ओर)। इससे प्रत्येक वस्तु को अधिकतम एक बार ही गिना जाता है — आगे की दिशा में पुनरावृत्ति करने पर किसी वस्तु को कई बार चुना जा सकता है।

def knapsack_01(weights, values, capacity):
    dp = [0] * (capacity + 1)
    for w, v in zip(weights, values):
        # Reverse order: prevents using the same item twice
        for c in range(capacity, w - 1, -1):
            dp[c] = max(dp[c], dp[c - w] + v)
    return dp[capacity]

weights = [1, 3, 4, 5]
values  = [1, 4, 5, 7]
cap = 7
print(knapsack_01(weights, values, cap))  # 9 (items 3+4: weight 3+4=7, value 4+5=9)

आगे बनाम उल्टी दिशा में पुनरावृत्ति

आंतरिक लूप में किस दिशा में पुनरावृत्ति करनी है, यह जानना अत्यंत महत्वपूर्ण है: 0/1 नैपसैक के लिए उल्टी दिशा (प्रत्येक वस्तु का अधिकतम एक बार उपयोग — पिछले अवस्थाओं को देखने से पुनः उपयोग रुकता है)। असीमित नैपसैक के लिए आगे की दिशा (प्रत्येक वस्तु का पुनः उपयोग किया जा सकता है — पहले से अद्यतन अवस्थाओं को देखने से कई उपयोग संभव होते हैं)। इसमें गलती करने पर 0/1 समस्या चुपचाप असीमित समस्या में या इसके विपरीत बदल जाती है। दिशा चुनने से पहले बाधा की हमेशा पुष्टि करें।

# 0/1 Knapsack: each item used AT MOST ONCE → iterate reverse
def knapsack_01_demo(weights, values, cap):
    dp = [0] * (cap + 1)
    for w, v in zip(weights, values):
        for c in range(cap, w-1, -1):  # REVERSE
            dp[c] = max(dp[c], dp[c-w] + v)
    return dp[cap]

# Unbounded Knapsack: items can be reused → iterate forward
def knapsack_unbounded(weights, values, cap):
    dp = [0] * (cap + 1)
    for c in range(1, cap + 1):
        for w, v in zip(weights, values):
            if c >= w:
                dp[c] = max(dp[c], dp[c-w] + v)  # FORWARD
    return dp[cap]

print(knapsack_01_demo([2,3],[3,4],5))     # 7
print(knapsack_unbounded([2,3],[3,4],5))   # 8 (use weight-2 twice: 3+3=6? or 4+... )

O(n) स्थान वाले अद्वितीय पथ

अद्वितीय पथों के लिए पूरी सारणी को एक ही पंक्ति से बदला जा सकता है। सभी खानों को 1 से प्रारम्भ करें (पहली पंक्ति)। इसके बाद की प्रत्येक पंक्ति के लिए बाएँ से दाएँ अद्यतन करें: dp[j] += dp[j-1]। विकर्ण की आवश्यकता नहीं है, क्योंकि पुनरावृत्ति संबंध केवल ऊपर वाले खाने (dp[j], अद्यतन से पहले का वर्तमान मान) और बाईं ओर वाले खाने (dp[j-1], जो पहले ही अद्यतन हो चुका है) का उपयोग करता है। यह 2D से 1D में संक्षिप्त करने का सबसे सरल तरीका है।

def unique_paths_opt(m, n):
    dp = [1] * n  # first row: all 1s
    for i in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j-1]  # above (dp[j]) + left (dp[j-1])
    return dp[n-1]

# With obstacles
def unique_paths_obstacles_opt(grid):
    m, n = len(grid), len(grid[0])
    dp = [0] * n
    dp[0] = 1
    for i in range(m):
        if grid[i][0] == 1: dp[0] = 0  # blocked column
        for j in range(1, n):
            if grid[i][j] == 1: dp[j] = 0  # blocked
            else: dp[j] += dp[j-1]
    return dp[n-1]

print(unique_paths_opt(3, 7))  # 28
print(unique_paths_obstacles_opt([[0,0,0],[0,1,0],[0,0,0]]))  # 2

जटिल पुनरावृत्ति संबंधों के लिए दो-पंक्ति बफ़र

जब पुनरावृत्ति संबंध में पिछली दो या अधिक पंक्तियों के खानों की आवश्यकता होती है (जैसे कुछ अंतराल DP रूपांतरों या 3D DP संक्षेपण में), तब दो-पंक्ति बफ़र का उपयोग किया जाता है: prev और curr सारणियाँ बनाए रखें और प्रत्येक पंक्ति के बाद उन्हें अदला-बदली करें। इससे O(2n) = O(n) स्थान मिलता है। k पंक्तियाँ पीछे देखने वाले पुनरावृत्ति संबंधों के लिए k सारणियों को चक्रीय बफ़र के रूप में बनाए रखें। यह एक-पंक्ति वाली गतिशील सारणी के प्रतिरूप का सामान्यीकरण है।

def lcs_two_row_buffer(s1, s2):
    m, n = len(s1), len(s2)
    prev = [0] * (n + 1)  # dp[i-1]
    curr = [0] * (n + 1)  # dp[i]
    for i in range(1, m + 1):
        curr[0] = 0
        for j in range(1, n + 1):
            if s1[i-1] == s2[j-1]:
                curr[j] = prev[j-1] + 1
            else:
                curr[j] = max(prev[j], curr[j-1])
        prev, curr = curr, prev  # swap (curr becomes prev)
    return prev[n]  # after swap, prev holds the last computed row

print(lcs_two_row_buffer('ABCBDAB', 'BDCABA'))  # 4

जब स्थान अनुकूलन संभव नहीं होता

स्थान अनुकूलन हमेशा संभव नहीं होता। यदि आपको इष्टतम समाधान का केवल मान नहीं, बल्कि उसे पुनर्निर्मित भी करना हो, तो आम तौर पर वापस लौटकर खोजने के लिए पूरी सारणी आवश्यक होती है। विकल्पों में शामिल हैं: (1) उसी आकार की अलग निर्णय-सारणी रखना। (2) हिर्शबर्ग एल्गोरिद्म का उपयोग करना, जो समस्या को मध्य-बिंदु पर बार-बार विभाजित करके पुनर्निर्माण सहित O(mn) समय और O(min(m,n)) स्थान में LCS निकालता है। (3) जब पुनर्निर्माण आवश्यक हो, तब O(mn) स्थान स्वीकार करना।

# When reconstruction needed: must keep full table or use Hirschberg
# Hirschberg's idea: compute LCS length in O(n) space at midpoint of s1,
# recurse on left and right halves. O(mn) time, O(n) space + reconstruction.

# For interview: mention the trade-off
# 'I can reduce to O(n) space if only the value is needed.
#  To also reconstruct the sequence, I need the full O(mn) table
#  or a more complex divide-and-conquer approach.'

print('Space opt: O(n) for length only')
print('Full table: O(mn) needed for reconstruction')

त्वरित जाँच

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

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

इस पाठ में आपने सीखा: जब केवल पिछली पंक्ति की आवश्यकता हो, तब 2D DP सारणियों को गतिशील 1D सारणी का उपयोग करके O(n) स्थान तक संक्षिप्त किया जा सकता है, विकर्ण चर का प्रतिरूप (अधिलेखित करने से पहले अस्थायी मान सहेजना) उन पुनरावृत्ति संबंधों को संभालता है जिनमें dp[i-1][j-1] की आवश्यकता होती है, और 0/1 नैपसैक में क्षमता पर उल्टी दिशा में, जबकि असीमित नैपसैक में आगे की दिशा में पुनरावृत्ति की जाती है। अब हम बैकट्रैकिंग प्रतिरूप का अध्ययन करेंगे: चुनें, खोजें, चयन हटाएँ — यह पूर्ण खोज एल्गोरिद्म का आधार है।

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

एआई शिक्षक के साथ कोडिंग साक्षात्कार की तैयारी सीखें — निःशुल्क

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

पाठ्यक्रम
90
पाठ
360

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

क्या “2D DP के लिए स्थान अनुकूलन” पाठ निःशुल्क है?

हाँ—“2D DP के लिए स्थान अनुकूलन” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और कोडिंग साक्षात्कार की तैयारी पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। कोडिंग साक्षात्कार की तैयारी पाठ्यक्रम में कुल 4 पाठ शामिल हैं।

“2D DP के लिए स्थान अनुकूलन” में मैं क्या सीखूँगा?

DP तालिका की केवल वर्तमान और पिछली पंक्तियाँ रखकर LCS और edit-distance का स्थान O(mn) से O(min(m,n)) तक घटाइए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ कोडिंग साक्षात्कार की तैयारी का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

क्या कोडिंग साक्षात्कार की तैयारी शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?

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

“2D DP के लिए स्थान अनुकूलन” पाठ पूरा करने में कितना समय लगता है?

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

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

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

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

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