2405. Optimal Partition of String - Explanation
Description
Split s into contiguous, nonempty pieces so that no piece contains a repeated letter. Every character must occur in exactly one piece. Return the smallest possible number of pieces.
Example 1:
Input: s = "a"
Output: 1Explanation: The only character forms one piece, "a".
Example 2:
Input: s = "ab"
Output: 1Explanation: The letters in "ab" are already distinct, so the whole string can stay in one piece.
Example 3:
Input: s = "aa"
Output: 2Explanation: The two occurrences of "a" cannot share a piece. Split the string into "a" and "a".
Constraints:
1 <= s.length <= 100000- Only lowercase English letters occur.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Greedy Algorithms - Making locally optimal choices (extend partition as long as possible) to achieve a global optimum
- HashSet Operations - Using sets for O(1) membership checks to detect duplicate characters
- Bit Manipulation - Using bitmasks to represent sets of characters efficiently with bitwise AND and OR operations
1. Greedy (Hash Set)
Intuition
To minimize the number of substrings, we want each substring to be as long as possible while containing only unique characters. A greedy approach works perfectly here: extend the current substring until we encounter a duplicate character, then start a new substring.
We use a hash set to track characters in the current substring. When we see a character already in the set, we've found a duplicate, so we increment our count and clear the set to start fresh.
Algorithm
- Initialize a set
curSetto track characters in the current partition andres = 1for the result count. - Iterate through each character in the string:
- If the character is already in
curSet, incrementresand clear the set. - Add the current character to
curSet.
- If the character is already in
- Return
res.
Time & Space Complexity
- Time complexity:
- Space complexity: since we have at most different characters.
2. Greedy (Array)
Intuition
Instead of using a set and clearing it on each partition, we can track the last index where each character appeared. A character causes a conflict only if its last occurrence is within the current partition (at or after the start index).
This approach avoids the overhead of clearing the set and uses constant extra space since we only need to track 26 lowercase letters.
Algorithm
- Create an array
lastIdxof size26initialized to-1, tracking the last seen index of each character. - Initialize
res = 1andstart = 0to mark the beginning of the current partition. - For each character at index
i:- If
lastIdx[char]>=start, this character appeared in the current partition, so start a new partition by settingstart = iand incrementingres. - Update
lastIdx[char] = i.
- If
- Return
res.
Time & Space Complexity
- Time complexity:
- Space complexity: since we have at most different characters.
3. Greedy (Bit Mask)
Intuition
Since we only have 26 lowercase letters, we can represent the set of characters in the current partition using a single integer as a bitmask. Each bit position corresponds to a letter (bit 0 for 'a', bit 1 for 'b', etc.).
This is the most space-efficient approach and uses fast bitwise operations to check membership and add characters.
Algorithm
- Initialize
res = 1andmask = 0. - For each character, compute its bit position
i = char - 'a'. - If the bit at position
iis already set inmask(i.e.,mask & (1 << i)is non-zero), we have a duplicate:- Reset
mask = 0and incrementres.
- Reset
- Set the bit for the current character:
mask |= (1 << i). - Return
res.
Time & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Starting the Result Counter at Zero
A common mistake is initializing res = 0 instead of res = 1. Since we are counting partitions and the string is non-empty, we always have at least one partition. Starting at zero would undercount by one because the final substring after the last split is never explicitly counted when we only increment on encountering duplicates.
Forgetting to Add the Character After Clearing the Set
When a duplicate is found, some implementations clear the set but forget to add the current character to the fresh set. This leads to incorrect behavior because the duplicate character that triggered the new partition should be the first character of that new partition.
Treating the Problem as Finding Unique Substrings
Some solvers confuse this with finding all unique substrings or counting unique characters globally. The goal is to partition the string such that each partition has unique characters within itself, not across the entire string. Each partition is independent and can reuse characters from previous partitions.
Sign in to join the discussion