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

ข้อ 17 · LC1493 Longest Subarray of 1's After Deleting One Element (ช่วงหนึ่งยาวสุดหลังลบตัว) 🟡

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

ต้องลบ element ออก 1 ตัวเสมอ หา subarray ของ 1 ต่อเนื่องที่ยาวที่สุดหลังลบ

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

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

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

เป็นน้องของ LC1004 โดยตรง: มองว่า "ต้องลบหนึ่งตัว" คือ "อนุญาตให้ window มี 0 ได้หนึ่งตัว (ตัวที่จะถูกลบ)" ก็ได้ Sliding Window ขนาดยืดหยุ่นแบบ k = 1 ทันที

ความต่างสำคัญคือการนับความยาว: โจทย์บังคับลบหนึ่งตัวเสมอ (แม้ไม่มี 0 เลย) คำตอบจึงเป็นความยาว window ลบหนึ่ง เขียน right - left แทน right - left + 1

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

ใช้ right - left ไม่ใช่ right - left + 1 เพราะบังคับลบหนึ่งตัวเสมอ กรณี [1, 1, 1] (ไม่มี 0) ต้องได้ 2 ไม่ใช่ 3 — สูตรนี้จัดการให้อัตโนมัติ

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

ลองไล่ nums = [1, 1, 0, 1] ดูค่า best (right - left)

right (ค่า)zerosหดซ้าย?leftความยาว (right-left)best
0 (1)0ไม่000
1 (1)0ไม่011
2 (0)1ไม่ (≤1)022
3 (1)1ไม่033

best = 3 (window ทั้งหมด [1,1,0,1] ลบ 0 ออกเหลือ 1 สามตัว)

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

print(longest_subarray([1, 1, 0, 1]))  # 3
print(longest_subarray([1, 1, 1]))     # 2
Output
3
2

โจทย์นี้เป็นน้องของ LC1004 โดยตรง: มองว่า "ต้องลบหนึ่งตัว" คือ "อนุญาตให้ window มี 0 ได้หนึ่งตัว (ตัวที่จะถูกลบ)" ก็ได้ variable size window แบบ k = 1 ทันที ขยายขวารับของเข้า ถ้ามี 0 เกินหนึ่งก็หดซ้ายจนเหลือไม่เกินหนึ่ง

ความต่างสำคัญคือการนับความยาว: โจทย์บังคับลบหนึ่งตัวเสมอ (แม้ไม่มี 0 เลย) คำตอบจึงเป็นความยาว window ลบหนึ่ง เขียน best = max(best, right - left) แทน right - left + 1 กรณี [1, 1, 1] (ไม่มี 0 เลย) เป็น edge case สำคัญ — ยังถูกบังคับลบ 1 ตัว คำตอบจึงเป็น 2 ไม่ใช่ 3 สูตรนี้จัดการให้อัตโนมัติ

Time O(n) แต่ละตัวเข้าออก window อย่างละครั้ง · Space O(1) ใช้ตัวแปรไม่กี่ตัว

💡 สรุป pattern

โจทย์ที่ดูต่าง แต่จริง ๆ คือ variable size window แบบ k = 1 ที่มีลูกเล่นเรื่องการนับความยาว: ถ้าเจอโจทย์ใกล้เคียง ให้ถามว่า "condition (เงื่อนไข) บน window คืออะไร" และ "ความยาวที่ต้องตอบนับยังไง"