2468. Split Message Based on Limit
Problem Description
You need to split a given string message
into multiple parts, where each part has a special suffix format <a/b>
.
The suffix format works as follows:
a
represents the current part number (starting from 1)b
represents the total number of parts- For example, if you split a message into 3 parts, the suffixes would be
<1/3>
,<2/3>
, and<3/3>
The splitting must follow these rules:
-
Length constraint: Each resulting part (including its suffix) must have a length exactly equal to
limit
, except for the last part which can have a length at mostlimit
. -
Message integrity: When you remove all the suffixes from the parts and concatenate them back together in order, you must get the original
message
string. -
Minimum parts: The split should use as few parts as possible.
-
Return value: Return an array of strings representing the split parts with their suffixes. If it's impossible to split the message according to these rules, return an empty array.
For example, if message = "hello world"
and limit = 10
:
- You might split it into parts like
"hello <1/2>"
and"world<2/2>"
- The first part has length 10 (exactly
limit
) - The second part has length 10 (exactly
limit
) - Removing the suffixes
<1/2>
and<2/2>
and concatenating gives back"hello world"
The challenge is that the suffix itself takes up space in each part, and the suffix length can vary depending on the total number of parts (since b
can be 1, 10, 100, etc., affecting the suffix length).
Intuition
The key insight is that we need to find the minimum number of parts k
that can accommodate the entire message. Since we want the minimum number of parts, we should try increasing values of k
starting from 1.
For any given number of parts k
, we need to determine if it's possible to fit the message. The challenge is that the suffix format <a/b>
takes up space, and this space varies depending on the number of digits in both a
and b
.
Let's think about what happens when we have k
parts:
- Each part needs a suffix in the format
<a/k>
wherea
goes from 1 tok
- The suffix length for part
j
is: 3 (for<
,/
,>
) + length of digits inj
+ length of digits ink
- The total space taken by all suffixes needs to be calculated
For k
parts, the total space available is limit × k
. From this total space, we need to subtract:
sa
: The sum of lengths of all part numbers (1, 2, ..., k). For example, ifk=12
, we need space for digits: 1,2,3,4,5,6,7,8,9,10,11,12. This gives us9×1 + 3×2 = 15
characters total.sb
: The space for the total numberk
repeated in each suffix. Sincek
appears in allk
parts, this islen(str(k)) × k
.sc
: The space for symbols (<
,/
,>
) in all parts, which is3 × k
.
The remaining space after subtracting these suffix components is what's available for the actual message content. If this remaining space is at least n
(the message length), then we can split the message into k
parts.
Once we find the minimum valid k
, we can construct the actual parts by:
- Taking chunks of the message
- Adding the appropriate suffix to each chunk
- Making sure each part (except possibly the last) has exactly
limit
characters
The reason we only need to check up to k = n
is that having more parts than characters in the message would be wasteful and unnecessary - we're looking for the minimum number of parts.
Learn more about Binary Search patterns.
Solution Approach
The solution implements the intuition by systematically checking each possible number of parts k
from 1 to n
.
For each value of k
, we calculate three key components:
-
sa
- Sum of all part indices: We accumulate the total length of all part numbers from 1 tok
. This is done incrementally usingsa += len(str(k))
. For example, whenk=10
,sa
includes the lengths of "1", "2", ..., "9" (9 characters) plus "10" (2 characters). -
sb
- Total length ofk
repeated: Since the total numberk
appears in every suffix, we calculatesb = len(str(k)) × k
. Fork=10
, each suffix contains "10", so we need2 × 10 = 20
characters for all the "10"s. -
sc
- Fixed symbols length: Every suffix has exactly 3 symbols (<
,/
,>
), so fork
parts:sc = 3 × k
.
The condition for feasibility is:
limit × k - (sa + sb + sc) >= n
This checks if the total available space minus the space needed for all suffixes can accommodate the entire message of length n
.
Once we find the smallest valid k
, we construct the result:
- Initialize an empty result array and a pointer
i = 0
to track our position in the message - For each part
j
from 1 tok
:- Create the suffix:
tail = f'<{j}/{k}>'
- Calculate how much message content can fit:
limit - len(tail)
- Extract that portion from the message:
message[i : i + limit - len(tail)]
- Combine content with suffix to create the complete part
- Update the pointer
i
to move forward in the message
- Create the suffix:
The algorithm returns the constructed array if a valid split is found, or an empty array if no valid split exists within the range [1, n]
.
The time complexity is O(n²)
in the worst case, where we might need to check all values of k
up to n
, and for each valid k
, we construct k
parts. The space complexity is O(n)
for storing the result array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with message = "hi there"
(length 8) and limit = 6
.
Step 1: Try k = 1 part
- We need suffix
<1/1>
which has length 5 (characters: '<', '1', '/', '1', '>') - Calculate space components:
sa = 1
(length of "1")sb = 1 × 1 = 1
(length of "1" × 1 occurrence)sc = 3 × 1 = 3
(3 symbols × 1 part)
- Total suffix space:
sa + sb + sc = 1 + 1 + 3 = 5
- Available space for message:
6 × 1 - 5 = 1
- Need space: 8 (message length)
- Since 1 < 8, k = 1 doesn't work ❌
Step 2: Try k = 2 parts
- We need suffixes
<1/2>
and<2/2>
, each with length 5 - Calculate space components:
sa = 1 + 1 = 2
(length of "1" + length of "2")sb = 1 × 2 = 2
(length of "2" × 2 occurrences)sc = 3 × 2 = 6
(3 symbols × 2 parts)
- Total suffix space:
sa + sb + sc = 2 + 2 + 6 = 10
- Available space for message:
6 × 2 - 10 = 2
- Need space: 8
- Since 2 < 8, k = 2 doesn't work ❌
Step 3: Try k = 3 parts
- We need suffixes
<1/3>
,<2/3>
,<3/3>
, each with length 5 - Calculate space components:
sa = 1 + 1 + 1 = 3
(lengths of "1", "2", "3")sb = 1 × 3 = 3
(length of "3" × 3 occurrences)sc = 3 × 3 = 9
(3 symbols × 3 parts)
- Total suffix space:
sa + sb + sc = 3 + 3 + 9 = 15
- Available space for message:
6 × 3 - 15 = 3
- Need space: 8
- Since 3 < 8, k = 3 doesn't work ❌
Step 4: Try k = 4 parts
- We need suffixes
<1/4>
,<2/4>
,<3/4>
,<4/4>
, each with length 5 - Calculate space components:
sa = 1 + 1 + 1 + 1 = 4
(lengths of "1", "2", "3", "4")sb = 1 × 4 = 4
(length of "4" × 4 occurrences)sc = 3 × 4 = 12
(3 symbols × 4 parts)
- Total suffix space:
sa + sb + sc = 4 + 4 + 12 = 20
- Available space for message:
6 × 4 - 20 = 4
- Need space: 8
- Since 4 < 8, k = 4 doesn't work ❌
Step 5: Try k = 5 parts
- We need suffixes
<1/5>
through<5/5>
, each with length 5 - Calculate space components:
sa = 1 + 1 + 1 + 1 + 1 = 5
(lengths of "1" through "5")sb = 1 × 5 = 5
(length of "5" × 5 occurrences)sc = 3 × 5 = 15
(3 symbols × 5 parts)
- Total suffix space:
sa + sb + sc = 5 + 5 + 15 = 25
- Available space for message:
6 × 5 - 25 = 5
- Need space: 8
- Since 5 < 8, k = 5 doesn't work ❌
Step 6: Try k = 6 parts
- Calculate space components:
sa = 1 + 1 + 1 + 1 + 1 + 1 = 6
sb = 1 × 6 = 6
sc = 3 × 6 = 18
- Total suffix space:
30
- Available space:
6 × 6 - 30 = 6
- Since 6 < 8, doesn't work ❌
Step 7: Try k = 7 parts
- Calculate space components:
sa = 1 + 1 + 1 + 1 + 1 + 1 + 1 = 7
sb = 1 × 7 = 7
sc = 3 × 7 = 21
- Total suffix space:
35
- Available space:
6 × 7 - 35 = 7
- Since 7 < 8, doesn't work ❌
Step 8: Try k = 8 parts
- Calculate space components:
sa = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 8
sb = 1 × 8 = 8
sc = 3 × 8 = 24
- Total suffix space:
40
- Available space:
6 × 8 - 40 = 8
- Since 8 = 8, k = 8 works! ✓
Constructing the result: Now we split "hi there" into 8 parts:
- Part 1: suffix
<1/8>
(5 chars) → can fit 1 char → "h<1/8>" - Part 2: suffix
<2/8>
(5 chars) → can fit 1 char → "i<2/8>" - Part 3: suffix
<3/8>
(5 chars) → can fit 1 char → " <3/8>" - Part 4: suffix
<4/8>
(5 chars) → can fit 1 char → "t<4/8>" - Part 5: suffix
<5/8>
(5 chars) → can fit 1 char → "h<5/8>" - Part 6: suffix
<6/8>
(5 chars) → can fit 1 char → "e<6/8>" - Part 7: suffix
<7/8>
(5 chars) → can fit 1 char → "r<7/8>" - Part 8: suffix
<8/8>
(5 chars) → can fit 1 char → "e<8/8>"
Final result: ["h<1/8>", "i<2/8>", " <3/8>", "t<4/8>", "h<5/8>", "e<6/8>", "r<7/8>", "e<8/8>"]
Each part has exactly 6 characters (meeting the limit), and removing all suffixes gives us back "hi there".
Solution Implementation
1class Solution:
2 def splitMessage(self, message: str, limit: int) -> List[str]:
3 """
4 Split a message into parts with suffix tags <current/total>.
5 Each part including the tag must not exceed the limit.
6
7 Args:
8 message: The original message string to split
9 limit: Maximum length for each part including the tag
10
11 Returns:
12 List of message parts with tags, or empty list if impossible
13 """
14 message_length = len(message)
15
16 # cumulative_digits tracks total digits used for part numbers 1 to k
17 cumulative_digits = 0
18
19 # Try different values of k (total number of parts)
20 for total_parts in range(1, message_length + 1):
21 # Add digits needed for current part number
22 cumulative_digits += len(str(total_parts))
23
24 # Calculate total overhead for all parts:
25 # digits_for_current: digits used for current part numbers (1, 2, ..., k)
26 digits_for_current = cumulative_digits
27 # digits_for_total: digits used for total (k appears k times)
28 digits_for_total = len(str(total_parts)) * total_parts
29 # brackets_and_slashes: 3 chars per part for '<', '/', '>'
30 brackets_and_slashes = 3 * total_parts
31
32 # Check if we have enough space for the actual message content
33 total_available_space = limit * total_parts
34 total_overhead = digits_for_current + digits_for_total + brackets_and_slashes
35
36 if total_available_space - total_overhead >= message_length:
37 # We can split the message with k parts
38 result = []
39 current_index = 0
40
41 # Create each part with its tag
42 for part_number in range(1, total_parts + 1):
43 # Create the suffix tag <current/total>
44 tag = f'<{part_number}/{total_parts}>'
45
46 # Calculate how much message content fits in this part
47 content_length = limit - len(tag)
48
49 # Extract message content and append tag
50 part_with_tag = message[current_index : current_index + content_length] + tag
51 result.append(part_with_tag)
52
53 # Move to next position in message
54 current_index += content_length
55
56 return result
57
58 # No valid split found
59 return []
60
1class Solution {
2 public String[] splitMessage(String message, int limit) {
3 int messageLength = message.length();
4 int sumOfPartNumbers = 0; // Sum of digits in part numbers (1, 2, ..., k)
5 String[] result = new String[0];
6
7 // Try different number of parts k from 1 to messageLength
8 for (int totalParts = 1; totalParts <= messageLength; ++totalParts) {
9 // Calculate the number of digits in totalParts
10 int digitsInTotalParts = String.valueOf(totalParts).length();
11
12 // Update sum of all part numbers' digits
13 sumOfPartNumbers += digitsInTotalParts;
14
15 // Calculate total digits needed for all "/k" portions
16 int sumOfTotalPartDigits = digitsInTotalParts * totalParts;
17
18 // Calculate total characters for brackets and slashes (3 per part: '<', '/', '>')
19 int totalBracketsAndSlashes = 3 * totalParts;
20
21 // Check if we can fit the entire message with k parts
22 // Total available space - overhead for formatting >= message length
23 if (limit * totalParts - (sumOfPartNumbers + sumOfTotalPartDigits + totalBracketsAndSlashes) >= messageLength) {
24 int currentIndex = 0;
25 result = new String[totalParts];
26
27 // Build each part with its suffix
28 for (int partNumber = 1; partNumber <= totalParts; ++partNumber) {
29 // Create suffix like "<1/5>"
30 String suffix = String.format("<%d/%d>", partNumber, totalParts);
31
32 // Calculate how much of the message can fit in this part
33 int availableSpace = limit - suffix.length();
34
35 // Extract message portion and append suffix
36 String part = message.substring(currentIndex,
37 Math.min(messageLength, currentIndex + availableSpace)) + suffix;
38 result[partNumber - 1] = part;
39
40 // Move to next position in message
41 currentIndex += availableSpace;
42 }
43 break;
44 }
45 }
46 return result;
47 }
48}
49
1class Solution {
2public:
3 vector<string> splitMessage(string message, int limit) {
4 int messageLength = message.size();
5 int sumOfCurrentIndices = 0; // Sum of digits in indices 1 to k
6 vector<string> result;
7
8 // Try different number of parts k
9 for (int numParts = 1; numParts <= messageLength; ++numParts) {
10 // Calculate the number of digits in current part number
11 int digitsInCurrentPart = to_string(numParts).size();
12
13 // Update sum of digits for all indices from 1 to numParts
14 sumOfCurrentIndices += digitsInCurrentPart;
15
16 // Calculate total digits needed for all part numbers in suffixes
17 int totalPartNumberDigits = digitsInCurrentPart * numParts;
18
19 // Calculate total characters for brackets and slashes (3 per part: "<", "/", ">")
20 int totalBracketChars = 3 * numParts;
21
22 // Check if current partition count can fit the message
23 // Total available space - total suffix overhead >= message length
24 if (numParts * limit - (sumOfCurrentIndices + totalPartNumberDigits + totalBracketChars) >= messageLength) {
25 int currentIndex = 0;
26
27 // Build each part with its suffix
28 for (int partNum = 1; partNum <= numParts; ++partNum) {
29 // Create suffix in format <partNum/numParts>
30 string suffix = "<" + to_string(partNum) + "/" + to_string(numParts) + ">";
31
32 // Extract message content and append suffix
33 string currentPart = message.substr(currentIndex, limit - suffix.size()) + suffix;
34 result.emplace_back(currentPart);
35
36 // Move to next segment of message
37 currentIndex += limit - suffix.size();
38 }
39 break;
40 }
41 }
42
43 return result;
44 }
45};
46
1function splitMessage(message: string, limit: number): string[] {
2 const messageLength: number = message.length;
3 let sumOfCurrentIndices: number = 0; // Sum of digits in indices 1 to k
4 const result: string[] = [];
5
6 // Try different number of parts k
7 for (let numParts = 1; numParts <= messageLength; numParts++) {
8 // Calculate the number of digits in current part number
9 const digitsInCurrentPart: number = numParts.toString().length;
10
11 // Update sum of digits for all indices from 1 to numParts
12 sumOfCurrentIndices += digitsInCurrentPart;
13
14 // Calculate total digits needed for all part numbers in suffixes
15 const totalPartNumberDigits: number = digitsInCurrentPart * numParts;
16
17 // Calculate total characters for brackets and slashes (3 per part: "<", "/", ">")
18 const totalBracketChars: number = 3 * numParts;
19
20 // Check if current partition count can fit the message
21 // Total available space - total suffix overhead >= message length
22 if (numParts * limit - (sumOfCurrentIndices + totalPartNumberDigits + totalBracketChars) >= messageLength) {
23 let currentIndex: number = 0;
24
25 // Build each part with its suffix
26 for (let partNum = 1; partNum <= numParts; partNum++) {
27 // Create suffix in format <partNum/numParts>
28 const suffix: string = `<${partNum}/${numParts}>`;
29
30 // Extract message content and append suffix
31 const currentPart: string = message.substring(currentIndex, currentIndex + limit - suffix.length) + suffix;
32 result.push(currentPart);
33
34 // Move to next segment of message
35 currentIndex += limit - suffix.length;
36 }
37 break;
38 }
39 }
40
41 return result;
42}
43
Time and Space Complexity
The time complexity is O(n × log n)
, where n
is the length of the string message
.
The analysis breaks down as follows:
- The outer loop iterates from 1 to
n
in the worst case, contributingO(n)
iterations - Inside each iteration, we compute
len(str(k))
multiple times, which takesO(log k)
time since the number of digits ink
is proportional tolog k
- When we find a valid
k
, we construct the answer by iterating throughk
parts, where each part involves string slicing and concatenation operations - The string slicing
message[i : i + limit - len(tail)]
takesO(limit)
time, and sincelimit
is bounded byn
, this isO(n)
in the worst case - However, the dominant factor is the outer loop with the logarithmic digit calculations, resulting in
O(n × log n)
overall complexity
The space complexity is O(1)
when ignoring the space consumption of the answer array. The only auxiliary space used consists of:
- Variables
n
,sa
,sb
,sc
,i
,j
,k
which are all integers taking constant space - The
tail
string which has a bounded size based on the number of digits ink
, which isO(log n)
- Since
O(log n)
is considered negligible compared to the input size, the space complexity excluding the output is effectivelyO(1)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Handling Variable Suffix Lengths
One of the most common mistakes is not accounting for the fact that suffix lengths change as the number of parts increases. For example:
- With 9 parts: suffixes are
<1/9>
,<2/9>
, ...,<9/9>
(all 5 characters) - With 10 parts: suffixes are
<1/10>
,<2/10>
, ...,<9/10>
,<10/10>
(5-7 characters)
Pitfall Example:
# WRONG: Assuming fixed suffix length
def splitMessage_wrong(message, limit):
n = len(message)
for k in range(1, n + 1):
suffix_len = len(f'<1/{k}>') # Wrong! This assumes all suffixes have same length
if (limit - suffix_len) * k >= n:
# This will fail for cases where part numbers have different digit counts
...
Solution: The correct approach tracks cumulative digits separately for part numbers and total count:
cumulative_digits = 0 # Sum of digits for 1, 2, ..., k
for total_parts in range(1, n + 1):
cumulative_digits += len(str(total_parts)) # Accumulate digits for each part number
digits_for_total = len(str(total_parts)) * total_parts # Total appears k times
2. Off-by-One Errors in Space Calculation
Another pitfall is making mistakes when calculating available space versus required space, especially with inequality conditions.
Pitfall Example:
# WRONG: Using wrong inequality or forgetting edge cases if total_space - overhead > message_length: # Should be >= not > # This misses cases where space exactly equals message length
Solution: Use the correct inequality that includes the boundary case:
if total_available_space - total_overhead >= message_length: # Correctly handles when available space exactly matches message length
3. Not Validating Individual Part Constraints
A subtle pitfall is assuming that if the total space works out, each individual part will automatically satisfy the limit constraint. While the algorithm's math ensures this, forgetting to verify can lead to issues.
Pitfall Example:
# WRONG: Not checking if any single suffix might exceed the limit
for k in range(1, n + 1):
# What if len(f'<{k}/{k}>') > limit?
# For example, if k=1000 and limit=5, the suffix alone is 11 characters!
Solution: Add an early check to ensure the suffix itself doesn't exceed the limit:
for total_parts in range(1, message_length + 1):
# Early termination if suffix alone would exceed limit
max_suffix_len = len(f'<{total_parts}/{total_parts}>')
if max_suffix_len > limit:
break # No point checking further as suffixes will only get longer
cumulative_digits += len(str(total_parts))
# ... rest of the logic
4. Integer Overflow in Large Calculations
When dealing with very large messages or limits, multiplication operations might cause issues in some languages (though Python handles big integers well).
Pitfall Example:
# In languages with fixed integer sizes, this might overflow: total_space = limit * total_parts # Could overflow for large values
Solution: Be mindful of the scale of operations and add bounds checking if necessary:
# Add reasonable bounds checking
if total_parts > message_length: # Can't need more parts than characters
break
if len(str(total_parts)) * 2 + 3 > limit: # Minimum suffix size check
break
Which type of traversal does breadth first search do?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!