On this page
ข้อ 17 · LC1493 Longest Subarray of 1's After Deleting One Element (ช่วงหนึ่งยาวสุดหลังลบตัว) 🟡
ต้องลบ element ออก 1 ตัวเสมอ หา subarray ของ 1 ต่อเนื่องที่ยาวที่สุดหลังลบ
โจทย์ (LC1493): กำหนด binary array (มีแค่ 0 กับ 1) ชื่อ nums ต้องลบสมาชิก (element) ออกจากอาร์เรย์ 1 ตัวเสมอ ให้ return ขนาดของ subarray ที่ไม่ว่างเปล่าและมีแต่ 1 ล้วนที่ยาวที่สุดในอาร์เรย์ที่เหลือ ถ้าไม่มี subarray แบบนั้นให้ return 0
- Input:
- nums = [1, 1, 0, 1]
- Output:
- 3
- Explanation:
- ลบ 0 (index 2) ออก เหลือ [1,1,1] ซึ่งเป็น 1 ต่อกัน 3 ตัวรวด
- 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 ตัวติดกัน
- Input:
- nums = [1, 1, 1]
- Output:
- 2
- Explanation:
- ไม่มี 0 ให้ลบเลย แต่โจทย์บังคับต้องลบ 1 ตัวอยู่ดี จึงเหลือ 1 ต่อกันแค่ 2 ตัว
- 1 <= nums.length <= 10^5
- nums[i] เป็น 0 หรือ 1 เท่านั้น
แนวทาง — ต้องใช้อะไร & คิดยังไง
เป็นน้องของ LC1004 โดยตรง: มองว่า "ต้องลบหนึ่งตัว" คือ "อนุญาตให้ window มี 0 ได้หนึ่งตัว (ตัวที่จะถูกลบ)" ก็ได้ Sliding Window ขนาดยืดหยุ่นแบบ k = 1 ทันที
ความต่างสำคัญคือการนับความยาว: โจทย์บังคับลบหนึ่งตัวเสมอ (แม้ไม่มี 0 เลย) คำตอบจึงเป็นความยาว window ลบหนึ่ง เขียน right - left แทน right - left + 1
- initialize left = 0, zeros = 0, best = 0
- iterate right ไปทุก index ถ้า nums[right] == 0 ให้ zeros += 1
- ขณะที่ zeros > 1 ให้หดซ้าย: ถ้า nums[left] == 0 ลด zeros แล้ว left += 1
- update best = max(best, right - left) — ไม่ +1 เพราะบังคับลบหนึ่งตัว
- จบ 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 | ไม่ | 0 | 0 | 0 |
| 1 (1) | 0 | ไม่ | 0 | 1 | 1 |
| 2 (0) | 1 | ไม่ (≤1) | 0 | 2 | 2 |
| 3 (1) | 1 | ไม่ | 0 | 3 | 3 |
best = 3 (window ทั้งหมด [1,1,0,1] ลบ 0 ออกเหลือ 1 สามตัว)
▶ เฉลยละเอียด (ลองเองก่อนนะ)
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])) # 23
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) ใช้ตัวแปรไม่กี่ตัว
โจทย์ที่ดูต่าง แต่จริง ๆ คือ variable size window แบบ k = 1 ที่มีลูกเล่นเรื่องการนับความยาว: ถ้าเจอโจทย์ใกล้เคียง ให้ถามว่า "condition (เงื่อนไข) บน window คืออะไร" และ "ความยาวที่ต้องตอบนับยังไง"