DSA Interview Prep · पाठ

उपस्ट्रिंग के लिए स्लाइडिंग विंडो

दोहराए गए अक्षरों के बिना सबसे लंबी उपस्ट्रिंग और सभी लक्ष्य अक्षरों वाली न्यूनतम विंडो खोजने के लिए परिवर्तनीय आकार की स्लाइडिंग विंडो लागू कीजिए।

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

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

स्लाइडिंग विंडो की अवधारणा

एक स्लाइडिंग विंडो बाएँ और दाएँ सूचक के बीच एक उप-सरणी (या उप-स्ट्रिंग) बनाए रखती है। प्रत्येक संभावित उप-सरणी के गुणों को O(n²) समय में नए सिरे से निकालने के बजाय, विंडो एक तत्व जोड़कर दाईं ओर फैलती है और एक तत्व हटाकर बाईं ओर सिकुड़ती है; इस दौरान हर चरण में O(1) समय में वर्तमान स्थिति बनाए रखी जाती है। इससे O(n) समय वाला एल्गोरिदम मिलता है। इसे 'स्लाइडिंग' इसलिए कहा जाता है क्योंकि यह पीछे जाए बिना सरणी में आगे बढ़ती रहती है।

# Fixed-size window sum: O(n) after O(k) setup
def max_sum_window(nums, k):
    window_sum = sum(nums[:k])  # initial window
    best = window_sum
    for i in range(k, len(nums)):
        window_sum += nums[i]       # add new right
        window_sum -= nums[i - k]   # remove old left
        best = max(best, window_sum)
    return best

print(max_sum_window([2,1,5,1,3,2], 3))  # 9  ([5,1,3])

निश्चित और परिवर्ती विंडो आकार

स्लाइडिंग विंडो के दो प्रकार होते हैं। निश्चित आकार वाली विंडो में दोनों सूचक एक ही गति से आगे बढ़ते हैं और विंडो में हमेशा ठीक k तत्व होते हैं। परिवर्ती आकार वाली विंडो में दायाँ सूचक लालचपूर्वक आगे बढ़ता है और बायाँ सूचक केवल तब सिकुड़ता है जब विंडो किसी बाधा का उल्लंघन करती है। परिवर्ती आकार वाली विंडो ऐसी समस्याएँ हल करती हैं जैसे 'दोहराव वाले वर्णों के बिना सबसे लंबी उप-स्ट्रिंग', जिसमें पहले से इष्टतम विंडो आकार ज्ञात नहीं होता।

# Variable window: longest substring with at most k distinct chars
def longest_k_distinct(s, k):
    from collections import defaultdict
    freq = defaultdict(int)
    left = 0
    best = 0
    for right in range(len(s)):
        freq[s[right]] += 1
        while len(freq) > k:    # window invalid: shrink
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

print(longest_k_distinct('eceba', 2))   # 3  ('ece')
print(longest_k_distinct('aa', 1))      # 2

दोहराव रहित सबसे लंबी उप-स्ट्रिंग

यह परिवर्ती स्लाइडिंग विंडो की सबसे प्रसिद्ध समस्या है। वर्तमान विंडो में मौजूद वर्णों को ट्रैक करने के लिए एक समुच्चय का उपयोग कीजिए। दाईं ओर विस्तार कीजिए; जब कोई दोहराया हुआ वर्ण मिले, तो बाईं ओर से तब तक सिकोड़िए जब तक वह दोहराव हट न जाए। तेज़ संस्करण में प्रत्येक वर्ण का नवीनतम सूचकांक रखने वाला हैश मानचित्र उपयोग होता है। इससे बायाँ सूचक धीरे-धीरे आगे बढ़ने के बजाय एक ही चरण में दोहराए गए वर्ण के आगे पहुँच जाता है।

def length_of_longest_substring(s):
    char_idx = {}  # char -> last seen index
    left = 0
    best = 0
    for right, c in enumerate(s):
        if c in char_idx and char_idx[c] >= left:
            left = char_idx[c] + 1  # jump past duplicate
        char_idx[c] = right
        best = max(best, right - left + 1)
    return best

print(length_of_longest_substring('abcabcbb'))  # 3 ('abc')
print(length_of_longest_substring('bbbbb'))     # 1
print(length_of_longest_substring('pwwkew'))    # 3 ('wke')

न्यूनतम विंडो वाली उप-स्ट्रिंग

s और t स्ट्रिंग दिए होने पर, s में t के सभी वर्णों वाली सबसे छोटी विंडो ढूँढिए। दो आवृत्ति मानचित्रों का उपयोग कीजिए: need (आवश्यक वर्ण) और have (वर्तमान विंडो में आवश्यकता पूरी करने वाले वर्ण)। t के कितने विशिष्ट वर्णों की आवश्यकता पूरी हो चुकी है, इसे ट्रैक कीजिए (formed गणक)। वर्णों को शामिल करने के लिए दाईं ओर विस्तार कीजिए; जब t के सभी वर्ण शामिल हो जाएँ, तो विंडो को छोटा करने के लिए बाईं ओर से सिकोड़िए। समय O(|s| + |t|)।

from collections import Counter

def min_window(s, t):
    if not t or not s: return ''
    need = Counter(t)
    have = {}
    formed = 0
    required = len(need)
    left = 0
    best = float('inf'), 0, 0
    for right, c in enumerate(s):
        have[c] = have.get(c, 0) + 1
        if c in need and have[c] == need[c]:
            formed += 1
        while formed == required:
            if right - left + 1 < best[0]:
                best = right - left + 1, left, right
            have[s[left]] -= 1
            if s[left] in need and have[s[left]] < need[s[left]]:
                formed -= 1
            left += 1
    return s[best[1]:best[2]+1] if best[0] != float('inf') else ''

print(min_window('ADOBECODEBANC', 'ABC'))  # 'BANC'

स्लाइडिंग विंडो का ढाँचा

अधिकांश परिवर्ती स्लाइडिंग विंडो समस्याएँ एक ही ढाँचे का पालन करती हैं: नया वर्ण शामिल करने के लिए दाईं ओर विस्तार कीजिए, विंडो की स्थिति अद्यतन कीजिए, वैधता जाँचिए और यदि विंडो अमान्य हो, तो उसके फिर से वैध होने तक बाईं ओर से सिकोड़ते जाइए। मुख्य समझ यह है कि बायाँ सूचक केवल आगे बढ़ता है — वह कभी पीछे नहीं जाता — इसलिए सिकोड़ने वाले सभी चरणों में कुल कार्य O(n) ही रहता है। विंडो प्रत्येक तत्व को अधिकतम दो बार देखती है (एक बार जोड़ते समय और एक बार हटाते समय)।

def sliding_window_template(s, condition_check, update_state, remove_state):
    """
    Generic sliding window skeleton.
    Adapt condition_check, update_state, remove_state per problem.
    """
    left = 0
    state = {}  # or whatever state you need
    best = 0
    for right in range(len(s)):
        update_state(state, s[right])      # expand window
        while not condition_check(state):  # window invalid
            remove_state(state, s[left])   # shrink window
            left += 1
        best = max(best, right - left + 1)
    return best

स्ट्रिंग में क्रमचय

जाँचिए कि क्या पैटर्न p का कोई क्रमचय s की उप-स्ट्रिंग के रूप में मौजूद है। क्रमचय की जाँच ऐसी विंडो के बराबर है जिसमें p के समान वर्ण-आवृत्तियाँ हों। ठीक len(p) वर्णों वाली स्लाइडिंग विंडो बनाए रखें और आवृत्ति-गणनाओं की तुलना करें। हर चरण में पूरे गणना-संग्रह ऑब्जेक्ट की तुलना O(26) समय लेती है (छोटे अंग्रेज़ी अक्षरों के लिए यह स्थिर है), इसलिए कुल समय O(n × 26) = O(n) होता है।

from collections import Counter

def check_inclusion(p, s):
    if len(p) > len(s): return False
    need  = Counter(p)
    window = Counter(s[:len(p)])
    if need == window: return True
    for right in range(len(p), len(s)):
        left = right - len(p)
        window[s[right]] += 1
        window[s[left]]  -= 1
        if window[s[left]] == 0:
            del window[s[left]]
        if window == need:
            return True
    return False

print(check_inclusion('ab', 'eidbaooo'))  # True ('ba')
print(check_inclusion('ab', 'eidboaoo'))  # False

एनाग्राम उप-स्ट्रिंग: सभी की गणना

s में p के एनाग्रामों के सभी आरंभिक सूचकांक ढूँढिए। यह स्ट्रिंग में क्रमचय वाली समस्या जैसी ही निश्चित-विंडो तकनीक है, लेकिन पहली मिलान वाली स्थिति पर True लौटाने के बजाय हम सभी मिलान वाली स्थितियाँ एकत्र करते हैं। विंडो का आकार len(p) पर निश्चित रहता है; हम इसे s पर आगे बढ़ाते हैं और हर चरण में आवृत्ति-गणनाओं की तुलना करते हैं।

from collections import Counter

def find_anagrams(s, p):
    result = []
    need = Counter(p)
    k = len(p)
    window = Counter(s[:k])
    if window == need:
        result.append(0)
    for right in range(k, len(s)):
        window[s[right]] += 1
        left_char = s[right - k]
        window[left_char] -= 1
        if window[left_char] == 0:
            del window[left_char]
        if window == need:
            result.append(right - k + 1)
    return result

print(find_anagrams('cbaebabacd', 'abc'))  # [0, 6]

अधिकतम 2 भिन्न वर्णों वाली सबसे लंबी उप-स्ट्रिंग

यह स्लाइडिंग विंडो का एक रूप है: अधिकतम 2 भिन्न वर्णों वाली सबसे लंबी उप-स्ट्रिंग ढूँढिए। वर्तमान विंडो के वर्णों का आवृत्ति मानचित्र बनाए रखें। जब मानचित्र में 2 से अधिक प्रविष्टियाँ हो जाएँ, तो बाएँ सूचक को दाईं ओर ले जाएँ (आवृत्ति घटाएँ और शून्य होने पर प्रविष्टि हटा दें), जब तक बाधा फिर से पूरी न हो जाए। यह 'अधिकतम k भिन्न वर्णों' वाली समस्या का k=2 वाला विशेष रूप है।

def longest_substring_two_distinct(s):
    from collections import defaultdict
    freq = defaultdict(int)
    left = 0
    best = 0
    for right, c in enumerate(s):
        freq[c] += 1
        while len(freq) > 2:
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

print(longest_substring_two_distinct('eceba'))     # 3  ('ece')
print(longest_substring_two_distinct('ccaabbb'))   # 5  ('aabbb')

स्लाइडिंग विंडो का अधिकतम

k आकार की प्रत्येक विंडो में अधिकतम मान ढूँढिए। हर विंडो का अधिकतम मान बलपूर्वक जाँचने में O(n×k) समय लगता है। सबसे अच्छा तरीका सूचकांकों वाले एकरूपी डेक का उपयोग करता है: घटते क्रम वाला डेक बनाए रखें, ताकि उसका अगला सिरा हमेशा वर्तमान विंडो के अधिकतम मान के सूचकांक पर हो। जब सूचकांक विंडो से बाहर निकलें, तो उन्हें अगले सिरे से हटाएँ; जब कोई बड़ा तत्व आए, तो पीछे के सिरे से छोटे सूचकांक हटाएँ। कुल समय O(n) है।

from collections import deque

def max_sliding_window(nums, k):
    dq = deque()  # stores indices, decreasing values
    result = []
    for i, n in enumerate(nums):
        # Remove indices outside window
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        # Maintain decreasing order
        while dq and nums[dq[-1]] < n:
            dq.pop()
        dq.append(i)
        if i >= k - 1:  # window is full
            result.append(nums[dq[0]])
    return result

print(max_sliding_window([1,3,-1,-3,5,3,6,7], 3))
# [3, 3, 5, 5, 6, 7]

स्लाइडिंग विंडो का उपयोग कब करें

जब आपको ये संकेत दिखाई दें, तो स्लाइडिंग विंडो का उपयोग कीजिए:

  • किसी बाधा वाली उप-स्ट्रिंग / उप-सरणी (अधिकतम लंबाई, योग = k, अधिकतम k भिन्न वर्ण)
  • समेकन वाली निश्चित आकार की विंडो (अधिकतम, योग, आवृत्ति)
  • सन्निहित श्रेणी से जुड़े प्रश्न (मनमाने उपसमुच्चय नहीं)
इनके लिए स्लाइडिंग विंडो का उपयोग न (NOT) करें: असन्निहित चयन, ऐसी समस्याएँ जिनमें सभी क्रमचय चाहिए (बैकट्रैकिंग का उपयोग करें), या ऐसी समस्याएँ जिनमें विंडो की स्थिति को क्रमिक रूप से बनाए नहीं रखा जा सकता। मुख्य जाँच यह है: क्या एक तत्व जोड़ने या हटाने पर स्थिति को O(1) समय में अद्यतन किया जा सकता है?

# Recognising sliding window problems:

# 1. Fixed window: 'maximum average of subarray of length k'
def max_avg(nums, k):
    s = sum(nums[:k])
    best = s
    for i in range(k, len(nums)):
        s += nums[i] - nums[i-k]
        best = max(best, s)
    return best / k

print(max_avg([1,12,-5,-6,50,3], 4))  # 12.75

# 2. Variable window: 'smallest subarray with sum >= target'
def min_sub_len(target, nums):
    left = s = 0
    best = float('inf')
    for right, n in enumerate(nums):
        s += n
        while s >= target:
            best = min(best, right - left + 1)
            s -= nums[left]; left += 1
    return 0 if best == float('inf') else best
print(min_sub_len(7, [2,3,1,2,4,3]))  # 2

मान्य विंडो की गणना: अधिकतम K

कुछ समस्याओं में ऐसी उप-सरणियों की संख्या पूछी जाती है जो किसी शर्त को पूरा करती हैं। एक उपयोगी युक्ति यह है: अधिकतम k भिन्न वर्णों वाली उप-सरणियों की संख्या गिनिए, फिर ठीक k पाने के लिए घटाइए: exactly(k) = at_most(k) - at_most(k-1)। इस गणना के प्रत्येक आह्वान में O(n) समय लगता है, इसलिए कुल समय O(n) रहता है। अधिकतम-k वाला फ़ंक्शन उन विंडो की गणना करता है जिनमें भिन्न वर्णों की संख्या k से अधिक नहीं होती। इसके लिए right - left + 1 का योग किया जाता है (हर right के लिए सभी मान्य left अंतिम बिंदु)।

from collections import defaultdict

def subarrays_at_most_k(s, k):
    freq = defaultdict(int)
    left = 0
    count = 0
    for right, c in enumerate(s):
        freq[c] += 1
        while len(freq) > k:
            freq[s[left]] -= 1
            if freq[s[left]] == 0: del freq[s[left]]
            left += 1
        count += right - left + 1  # all valid windows ending at right
    return count

def subarrays_exactly_k(s, k):
    return subarrays_at_most_k(s, k) - subarrays_at_most_k(s, k-1)

print(subarrays_exactly_k('araaci', 2))  # 9

त्वरित जाँच

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

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

इस पाठ में आपने सीखा: स्लाइडिंग विंडो O(n²) समय को समाप्त करती है, क्योंकि वह एक चलती हुई विंडो की स्थिति बनाए रखती है और तत्वों के जुड़ने या हटने पर उसे O(1) समय में अद्यतन करती है, निश्चित आकार वाली विंडो में दोनों सूचक एक ही गति से आगे बढ़ते हैं; परिवर्ती आकार वाली विंडो दाईं ओर लालचपूर्वक फैलती है और केवल बाधा का उल्लंघन होने पर बाईं ओर सिकुड़ती है, और न्यूनतम विंडो वाली उप-स्ट्रिंग तथा स्ट्रिंग में क्रमचय — दोनों आवृत्ति-मानचित्र वाली विंडो स्थिति का उपयोग करते हैं, जिसमें एक गणक यह ट्रैक करता है कि कितने आवश्यक वर्णों की आवश्यकता अभी पूरी हुई है। आगे हम एनाग्राम और वर्ण-आवृत्ति मानचित्रों का अध्ययन करेंगे।

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

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

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

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

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

क्या “उपस्ट्रिंग के लिए स्लाइडिंग विंडो” पाठ निःशुल्क है?

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

“उपस्ट्रिंग के लिए स्लाइडिंग विंडो” में मैं क्या सीखूँगा?

दोहराए गए अक्षरों के बिना सबसे लंबी उपस्ट्रिंग और सभी लक्ष्य अक्षरों वाली न्यूनतम विंडो खोजने के लिए परिवर्तनीय आकार की स्लाइडिंग विंडो लागू कीजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ DSA Interview Prep का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।

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

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

“उपस्ट्रिंग के लिए स्लाइडिंग विंडो” पाठ पूरा करने में कितना समय लगता है?

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

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

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

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

  1. साक्षात्कारों के लिए Python स्ट्रिंग API
  2. उपस्ट्रिंग के लिए स्लाइडिंग विंडो
  3. Anagram और अक्षर-आवृत्ति मानचित्र
  4. स्ट्रिंग एन्कोडिंग, उलटना और Palindrome
← DSA Interview Prep पर वापस जाएँ