On this page
ข้อ 15 · LC1456 Maximum Number of Vowels in a Substring of Given Length (นับสระในหน้าต่าง k) 🟡
หา substring ยาว k ที่มีตัวสระมากที่สุด แล้วคืนจำนวนสระนั้น
โจทย์ (LC1456): กำหนด string s และเลขจำนวนเต็ม k ให้หาจำนวน vowel (สระในภาษาอังกฤษ: a, e, i, o, u) ที่มากที่สุด ที่สามารถปรากฏได้ใน substring ใด ๆ ของ s ที่มีความยาวเท่ากับ k พอดี
- Input:
- s = "abciiidef", k = 3
- Output:
- 3
- Explanation:
- ช่วง "iii" มีสระครบทั้ง 3 ตัว เป็น substring ยาว 3 ที่มีสระมากที่สุดที่เป็นไปได้
- Input:
- s = "aeiou", k = 2
- Output:
- 2
- Explanation:
- ทุกตัวอักษรเป็นสระ ดังนั้น substring ยาว 2 ช่วงไหนก็มีสระครบ 2 ตัวเท่ากันหมด
- Input:
- s = "leetcode", k = 3
- Output:
- 2
- Explanation:
- ช่วง "lee", "eet" หรือ "ode" ต่างมีสระ 2 ตัว ซึ่งเป็นค่ามากที่สุดที่หาได้ในสตริงนี้
- 1 <= s.length <= 10^5
- s เป็นตัวอักษรอังกฤษพิมพ์เล็ก
- 1 <= k <= s.length
แนวทาง — ต้องใช้อะไร & คิดยังไง
ใช้ Sliding Window ขนาดคงที่ k เหมือนข้อก่อน แต่แทนที่จะเก็บผลรวม เรา track "จำนวน vowel ใน window" แทน เช็คว่าตัวอักษรเป็นสระด้วย set("aeiou") (hash set) ทำให้เช็คได้ O(1)
brute force นับสระใหม่ในทุก substring ยาว k เป็น O(nk) ช้า Sliding Window เก็บ count แล้วปรับทีละหนึ่งตอนเลื่อน จึงเหลือ O(n)
- เตรียม set ของสระ นับสระใน window แรก s[:k] เก็บใน count แล้ว initialize best = count
- iterate i จาก k ไปจนจบ string
- ถ้าตัวใหม่ s[i] เป็นสระ count += 1
- ถ้าตัวเก่า s[i - k] เป็นสระ count -= 1
- update best = max(best, count)
- (ปรับให้เร็ว) ถ้า best == k หยุดได้เลย เพราะสระเยอะสุดใน window ยาว k คือ k
ตัวที่ต้องถอดคือ s[i - k] (ห่างไป k ช่อง) ไม่ใช่ s[i - k + 1] — ใช้ index ผิดจะนับเพี้ยนทันที
ไล่ทีละสเต็ป
ลองไล่ s = "leetcode", k = 3 window แรก "lee" มีสระ 2 (e, e)
| i | ตัวเข้า s[i] | ตัวออก s[i-k] | count หลังปรับ | best |
|---|---|---|---|---|
| เริ่ม | - | - | 2 (lee) | 2 |
| 3 | t (ไม่ใช่สระ) | l (ไม่ใช่สระ) | 2 (eet) | 2 |
| 4 | c (ไม่ใช่สระ) | e (สระ) | 1 (etc) | 2 |
| 5 | o (สระ) | e (สระ) | 1 (tco) | 2 |
| 6 | d (ไม่ใช่สระ) | t (ไม่ใช่สระ) | 1 (cod) | 2 |
| 7 | e (สระ) | c (ไม่ใช่สระ) | 2 (ode) | 2 |
best = 2 ตลอด คืนค่า 2
▶ เฉลยละเอียด (ลองเองก่อนนะ)
def max_vowels(s, k):
vowels = set("aeiou")
count = sum(1 for c in s[:k] if c in vowels) # นับสระในหน้าต่างแรก
best = count
for i in range(k, len(s)):
if s[i] in vowels: # ตัวใหม่เข้าทางขวา
count += 1
if s[i - k] in vowels: # ตัวเก่าหลุดออกทางซ้าย
count -= 1
best = max(best, count)
if best == k: # เต็มหน้าต่างแล้ว ไม่มีทางมากกว่านี้
break
return best
print(max_vowels("abciiidef", 3)) # 3
print(max_vowels("leetcode", 3)) # 23
2แทนที่จะนับสระใหม่ทุก window (ช้า O(nk)) เรา track count แล้วปรับทีละหนึ่งตอนเลื่อน: ตัวใหม่เข้าเป็นสระ +1, ตัวเก่าที่หลุดออก (s[i - k]) เป็นสระ -1 ใช้ set("aeiou") เช็คว่าเป็นสระ O(1)
if best == k แล้ว break เป็นการปรับให้เร็วขึ้น เพราะสระมากสุดใน window ยาว k คือ k อยู่แล้ว เจอครบก็ไม่ต้องดูต่อ เอาออกก็ยังถูก เพียงแต่ iterate ครบทุกตัว
Time O(n) iterate string รอบเดียว · Space O(1) hash set ของสระมีแค่ 5 ตัว ถือเป็นค่าคงที่
window ขนาดคงที่ที่ track "count" แทนผลรวม: ตัวเข้าเพิ่มนับ ตัวออกลดนับ pattern เดียวกับผลรวมแต่เปลี่ยนสิ่งที่ track เท่านั้น