โจทย์: Partition Labels
ให้สตริง S ประกอบด้วยตัวอักษรพิมพ์เล็ก ต้องแบ่งสตริงออกเป็นจำนวนส่วน (parts) มากที่สุดเท่าที่จะทำได้ โดยที่ แต่ละตัวอักษรต้องปรากฏในได้สูงสุด 1 ส่วนเท่านั้น ส่งคืนลิสต์ของจำนวนเต็มที่แทนขนาดของแต่ละส่วน
ตัวอย่าง: S = "ababcbacadefegdehijhklij"
ผลลัพธ์: [9, 7, 8] เพราะแบ่งได้เป็น "ababcbaca", "defegde", "hijhklij" ตัวอักษรแต่ละตัวอยู่ในส่วนเดียวเท่านั้น
อัลกอริทึม (Greedy + Two Pointers)
- สร้าง map
last_indexเก็บ ตำแหน่งสุดท้าย ของแต่ละตัวอักษรในสตริง - กำหนด
start = 0(เริ่มต้นของ chunk ปัจจุบัน),end = 0(สิ้นสุดของ chunk ปัจจุบัน) - วนลูป
iจาก 0 ถึง n-1:- อัปเดต
end = max(end, last_index[s[i]])(ขยายขอบเขต chunk ให้ครอบคลุมตัวอักษรนี้) - หาก
i == endแสดงว่าจบ chunk แล้ว:- บันทึกขนาด chunk:
i - start + 1 - อัปเดต
start = i + 1(เริ่ม chunk ใหม่)
- บันทึกขนาด chunk:
- อัปเดต
- ส่งคืนลิสต์ขนาด chunks
ตัวอย่างโค้ด C++
#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<int> v) {
cout << "[";
for (int i = 0; i < v.size(); i++) {
cout << v[i];
if (i + 1 < v.size()) cout << ", ";
}
cout << "]" << endl;
}
class Solution {
public:
vector<int> partitionLabels(string s) {
// ขั้นตอนที่ 1: หาตำแหน่งสุดท้ายของแต่ละตัวอักษร
unordered_map<char, int> last_index;
for (int i = 0; i < s.size(); i++)
last_index[s[i]] = i;
vector<int> ans;
int start = 0, end = 0;
// ขั้นตอนที่ 2: Greedy partitioning
for (int i = 0; i < s.size(); i++) {
end = max(end, last_index[s[i]]);
if (i == end) {
ans.push_back(i - start + 1);
start = i + 1;
}
}
return ans;
}
};
int main() {
Solution ob;
print_vector(ob.partitionLabels("ababcbacadefegdehijhklij"));
return 0;
}
Input
"ababcbacadefegdehijhklij"
Output
[9, 7, 8]
ความซับซ้อน (Complexity)
- เวลา: O(n) - วนลูปผ่านสตริง 2 รอบ
- พื้นที่: O(1) - map เก็บได้สูงสุด 26 ตัวอักษร (คงที่)