DSA Interview Prep · Pelajaran

Tetingkap Gelangsar untuk Subrentetan

Laksanakan tetingkap gelangsar bersaiz berubah untuk mencari subrentetan terpanjang tanpa aksara berulang dan tetingkap minimum yang mengandungi semua aksara sasaran.

Pelajaran 2 daripada 413 langkah

Tetingkap Gelangsar untuk Subrentetan ialah pelajaran DSA Interview Prep percuma di CoddyKit. Ini ialah pelajaran 2 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran DSA Interview Prep, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus DSA Interview Prep merangkumi sejumlah 4 pelajaran.

Konsep Tetingkap Gelongsor

Tetingkap gelongsor mengekalkan sublarik (atau subrentetan) antara penuding kiri dan kanan. Daripada mengira semula sifat setiap sublarik yang mungkin dari awal dalam O(n²), tetingkap mengembang ke kanan dengan menambah satu unsur dan mengecut ke kiri dengan membuang satu unsur, sambil mengekalkan keadaan semasa dalam O(1) bagi setiap langkah. Hasilnya ialah algoritma O(n). Tetingkap ini dipanggil 'gelongsor' kerana ia bergerak ke hadapan melalui tatasusunan tanpa bergerak ke belakang.

# 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])

Saiz Tetingkap Tetap berbanding Berubah

Terdapat dua variasi tetingkap gelongsor. Dalam tetingkap bersaiz tetap, kedua-dua penuding bergerak pada kadar yang sama dan tetingkap sentiasa mempunyai tepat k unsur. Dalam tetingkap bersaiz berubah, penuding kanan mengembang secara agresif dan penuding kiri mengecut hanya apabila tetingkap melanggar kekangan. Tetingkap bersaiz berubah menyelesaikan masalah seperti 'subrentetan terpanjang tanpa aksara berulang' apabila saiz tetingkap optimum belum diketahui.

# 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

Subrentetan Terpanjang Tanpa Pengulangan

Ini ialah masalah tetingkap gelongsor berubah yang paling terkenal. Gunakan himpunan untuk menjejaki aksara dalam tetingkap semasa. Kembangkan ke kanan; apabila pendua ditemui, kecilkan dari kiri sehingga pendua itu dibuang. Versi yang lebih pantas menggunakan peta cincang yang menyimpan indeks terkini bagi setiap aksara, lalu membolehkan penuding kiri melangkaui pendua dalam satu langkah dan bukannya bergerak sedikit demi sedikit.

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')

Subrentetan Tetingkap Minimum

Diberi rentetan s dan t, cari tetingkap terkecil dalam s yang mengandungi semua aksara t. Gunakan dua peta frekuensi: need (aksara yang diperlukan) dan have (aksara dalam tetingkap semasa yang memenuhi keperluan). Jejaki bilangan aksara unik dalam t yang telah dipenuhi (pembilang formed). Kembangkan ke kanan untuk memasukkan aksara; apabila seluruh t telah diliputi, kecilkan dari kiri untuk meminimumkan tetingkap. Masa 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'

Rangka Tetingkap Gelongsor

Kebanyakan masalah tetingkap gelongsor berubah berkongsi rangka yang sama: kembangkan ke kanan untuk memasukkan aksara baharu, kemas kini keadaan tetingkap, semak kesahan, dan jika tidak sah, kecilkan dari kiri sehingga kembali sah. Wawasan utamanya ialah penuding kiri hanya bergerak ke hadapan — ia tidak pernah bergerak ke belakang — maka jumlah kerja bagi semua langkah pengecilan ialah O(n). Tetingkap memproses setiap unsur paling banyak dua kali, iaitu sekali ketika ditambah dan sekali ketika dibuang.

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

Permutasi dalam Rentetan

Semak sama ada sebarang permutasi corak p wujud sebagai subrentetan s. Semakan permutasi bersamaan dengan mencari tetingkap yang mempunyai frekuensi aksara yang sama seperti p. Kekalkan tetingkap yang mengandungi tepat len(p) aksara dan bandingkan kiraan frekuensi. Membandingkan keseluruhan objek pengira pada setiap langkah memerlukan O(26), iaitu pemalar bagi bahasa Inggeris huruf kecil, lalu memberikan O(n × 26) = O(n) secara keseluruhan.

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

Subrentetan Anagram: Kira Kesemuanya

Cari semua indeks permulaan anagram p dalam s. Ini menggunakan teknik tetingkap tetap yang sama seperti permutasi dalam rentetan, tetapi bukannya mengembalikan Benar pada padanan pertama, kita mengumpulkan semua kedudukan yang sepadan. Saiz tetingkap ditetapkan kepada len(p); gerakkan tetingkap merentasi s dan bandingkan kiraan frekuensi pada setiap langkah.

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]

Subrentetan Terpanjang dengan Tidak Lebih daripada 2 Aksara Berbeza

Ini ialah variasi tetingkap gelongsor: cari subrentetan terpanjang yang mengandungi tidak lebih daripada 2 aksara berbeza. Kekalkan peta frekuensi aksara dalam tetingkap semasa. Apabila peta mempunyai lebih daripada 2 entri, gerakkan penuding kiri ke kanan, kurangkan frekuensi dan padamkan entri jika nilainya sifar, sehingga kekangan dipenuhi semula. Ini ialah kes khas masalah 'paling banyak k aksara berbeza' dengan 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')

Maksimum Tetingkap Gelongsor

Cari nilai maksimum dalam setiap tetingkap bersaiz k. Semakan secara kasar bagi maksimum setiap tetingkap mengambil masa O(n×k). Pendekatan optimum menggunakan dek monotonik yang menyimpan indeks: kekalkan dek dalam susunan menurun supaya bahagian hadapan sentiasa mengandungi indeks maksimum tetingkap semasa. Buang indeks dari hadapan apabila indeks tersebut keluar dari tetingkap, dan buang indeks dari belakang apabila unsur yang lebih besar masuk. Jumlah masa ialah 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]

Bilakah Perlu Menggunakan Tetingkap Gelongsor

Gunakan tetingkap gelongsor apabila anda melihat:

  • Subrentetan / sublarik dengan kekangan (panjang maksimum, jumlah = k, paling banyak k aksara berbeza)
  • Saiz tetingkap tetap dengan pengagregatan (maksimum, jumlah, frekuensi)
  • Soalan tentang julat bersebelahan (bukan subset sewenang-wenangnya)
JANGAN gunakan tetingkap gelongsor untuk: pemilihan yang tidak bersebelahan, masalah yang memerlukan semua permutasi (gunakan penjejakan balik), atau masalah yang keadaannya tidak boleh dikemas kini secara berperingkat. Ujian utamanya: bolehkah anda mengemas kini keadaan dalam O(1) apabila menambah atau membuang satu unsur?

# 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

Mengira Tetingkap Sah: Paling Banyak K

Sesetengah masalah meminta bilangan sublarik yang memenuhi sesuatu syarat. Satu helah yang berguna ialah mengira sublarik dengan paling banyak k aksara berbeza, kemudian menolaknya untuk mendapatkan tepat k: exactly(k) = at_most(k) - at_most(k-1). Setiap panggilan kepada fungsi tersebut mengambil masa O(n), lalu jumlah masa ialah O(n). Fungsi itu mengira tetingkap yang bilangan aksara berbezanya tidak melebihi k dengan menjumlahkan right - left + 1 (semua titik kiri yang sah bagi setiap titik kanan).

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

Semakan Pantas

Uji pemahaman anda tentang konsep Struktur Data & Algoritma — Persediaan Temu Duga Pengekodan daripada pelajaran ini.

Rumusan Pelajaran

Dalam pelajaran ini, anda mempelajari: tetingkap gelongsor menghapuskan O(n²) dengan mengekalkan keadaan tetingkap semasa yang dikemas kini dalam O(1) apabila unsur masuk dan keluar, tetingkap bersaiz tetap menggerakkan kedua-dua penuding pada kadar yang sama; tetingkap bersaiz berubah mengembang ke kanan secara agresif dan mengecut ke kiri hanya apabila kekangan dilanggar, dan subrentetan tetingkap minimum serta permutasi dalam rentetan kedua-duanya menggunakan keadaan tetingkap berasaskan peta frekuensi dengan pembilang yang menjejaki bilangan aksara yang diperlukan dan telah dipenuhi. Seterusnya, kita akan meneroka anagram dan peta frekuensi aksara.

Percuma untuk bermula

Pelajari Python dengan tutor kecerdasan buatan — percuma

Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.

Kursus
30
Pelajaran
120

Soalan Lazim

Adakah pelajaran “Tetingkap Gelangsar untuk Subrentetan” percuma?

Ya — sebanyak 3 pelajaran dalam laluan pembelajaran DSA Interview Prep, termasuk “Tetingkap Gelangsar untuk Subrentetan”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus DSA Interview Prep merangkumi sejumlah 4 pelajaran.

Apakah yang akan saya pelajari dalam “Tetingkap Gelangsar untuk Subrentetan”?

Laksanakan tetingkap gelangsar bersaiz berubah untuk mencari subrentetan terpanjang tanpa aksara berulang dan tetingkap minimum yang mengandungi semua aksara sasaran. Anda berlatih DSA Interview Prep menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.

Adakah saya memerlukan pengalaman untuk memulakan DSA Interview Prep?

Tiada pengalaman terdahulu diperlukan. Pembelajaran DSA Interview Prep di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 2 daripada 4.

Berapa lamakah pelajaran “Tetingkap Gelangsar untuk Subrentetan” diambil?

Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.

Bolehkah saya menulis dan menjalankan kod dalam pelajaran DSA Interview Prep ini?

Ya. Setiap pelajaran DSA Interview Prep menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.

Semua pelajaran dalam kursus ini

  1. API Rentetan Python untuk Temu Duga
  2. Tetingkap Gelangsar untuk Subrentetan
  3. Anagram dan Peta Kekerapan Aksara
  4. Pengekodan Rentetan, Pembalikan dan Palindrom
← Kembali ke DSA Interview Prep