Notes & software courses · Free to learn
Aph's Blog
On this page

ข้อ 16 · LC1004 Max Consecutive Ones III (หนึ่งต่อเนื่องมากสุด พลิก k) 🟡

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

พลิก 0 เป็น 1 ได้มากสุด k ตัว หาช่วง 1 ต่อเนื่องที่ยาวที่สุด

โจทย์ (LC1004): กำหนด binary array (มีแค่ 0 กับ 1) ชื่อ nums และเลขจำนวนเต็ม k ให้ return จำนวนสูงสุดของ 1 ที่ต่อเนื่องกันในอาร์เรย์ หากสามารถพลิก (flip) 0 เป็น 1 ได้อย่างมากที่สุด k ตัว

Example 1
Input:
nums = [1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], k = 2
Output:
6
Explanation:
พลิก 0 สองตัวตรงกลาง (index 4-5) เป็น 1 จะได้ช่วง [1,1,1,1,1,1] ยาว 6 ตัวติดกัน — ยาวที่สุดเท่าที่พลิกได้ 2 ตัว
Example 2
Input:
nums = [0, 0, 0], k = 0
Output:
0
Explanation:
k = 0 พลิกไม่ได้เลยสักตัว และไม่มี 1 อยู่ในอาร์เรย์เดิม จึงไม่มีช่วง 1 ต่อเนื่องแม้แต่ตัวเดียว
Constraints (ข้อจำกัด)
  • 1 <= nums.length <= 10^5
  • nums[i] เป็น 0 หรือ 1 เท่านั้น
  • 0 <= k <= nums.length

แนวทาง — ต้องใช้อะไร & คิดยังไง

ใช้ Sliding Window ขนาดยืดหยุ่น (variable size) กุญแจคือเปลี่ยนมุมมอง: "พลิก 0 ได้ k ตัว" เท่ากับ "window ที่ยาวที่สุดที่มี 0 ไม่เกิน k ตัว" เพราะ 0 ทุกตัวใน window พลิกเป็น 1 ได้ตราบใดที่ไม่เกิน k

brute force ลองทุกช่วงแล้วนับ 0 เป็น O(n²) ช้า Sliding Window ขยายขวารับของเข้าเรื่อย ๆ track จำนวน 0 ไว้ ถ้าเกิน k ค่อยหดซ้าย จึงเหลือ O(n)

  1. initialize left = 0, zeros = 0, best = 0
  2. iterate right ไปทุก index ถ้า nums[right] == 0 ให้ zeros += 1 (รับตัวใหม่ทางขวา)
  3. ขณะที่ zeros > k ให้หดซ้าย: ถ้า nums[left] == 0 ลด zeros แล้ว left += 1
  4. window ถูกต้องแล้ว update best = max(best, right - left + 1)
  5. จบ loop return best
จุดพลาดที่พบบ่อย

ต้องใช้ while ไม่ใช่ if ตอนหดซ้าย และความยาว window คือ right - left + 1 (บวกหนึ่งเพราะรวมทั้งสองปลาย)

ไล่ทีละสเต็ป

ลองไล่ nums = [1, 1, 0, 0, 1], k = 1 ดูว่า left หดตอนไหน

right (ค่า)zerosหดซ้าย?leftความยาว (right-left+1)best
0 (1)0ไม่011
1 (1)0ไม่022
2 (0)1ไม่ (≤k)033
3 (0)2ใช่ จน zeros≤1313
4 (1)1ไม่323

best = 3 (ช่วง [1, 1, 0] พลิก 0 หนึ่งตัวได้ 1 สามตัว)

▶ เฉลยละเอียด (ลองเองก่อนนะ)
เฉลย (Python) — โค้ดนี้รันได้จริงpython
def longest_ones(nums, k):
    left = 0
    zeros = 0            # จำนวน 0 ในหน้าต่างตอนนี้
    best = 0
    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1                 # รับตัวใหม่ทางขวา
        while zeros > k:               # 0 เกินโควตาพลิก
            if nums[left] == 0:
                zeros -= 1             # หดซ้าย ถอด 0 ออก
            left += 1
        best = max(best, right - left + 1)   # หน้าต่างตอนนี้ถูกต้อง
    return best

print(longest_ones([1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], 2))  # 6
Output
6

กุญแจคือเปลี่ยนมุมมอง: แทนที่จะคิดว่า "พลิก 0 กี่ตัว" ให้คิดว่า "window ที่ยาวที่สุดที่มี 0 ไม่เกิน k ตัว" มีค่าเท่ากัน เพราะ 0 ทุกตัวใน window พลิกเป็น 1 ได้ตราบใดที่ไม่เกิน k เราจึงขยายขวารับของเข้าเรื่อย ๆ และ track จำนวน 0 ไว้

เมื่อ 0 ใน window เกิน k เราหดซ้าย (ขยับ left) จนจำนวน 0 กลับมาไม่เกิน k left ขยับรวมกันไม่เกิน n ครั้งตลอด loop จึงยังเป็น O(n) ไม่ใช่ O(n²) ถ้าเผลอใช้ if แทน while จะหด 0 ไม่พอเมื่อ k ลดหลายตัว

Time O(n) แต่ละตัวเข้าและออก window อย่างละครั้ง · Space O(1) track แค่ตัวนับกับ pointer (ตัวชี้)

💡 สรุป pattern

window ขนาดยืดหยุ่น + "เปลี่ยนโจทย์เป็น condition (เงื่อนไข) บน window": พลิก k ตัว = window ที่มี 0 ไม่เกิน k ขยายขวาเสมอ หดซ้ายเมื่อผิด condition