Facebook Pixel

2468. Split Message Based on Limit

HardStringBinary SearchEnumeration
Leetcode Link

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:

  1. 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 most limit.

  2. 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.

  3. Minimum parts: The split should use as few parts as possible.

  4. 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).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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> where a goes from 1 to k
  • The suffix length for part j is: 3 (for <, /, >) + length of digits in j + length of digits in k
  • 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:

  1. sa: The sum of lengths of all part numbers (1, 2, ..., k). For example, if k=12, we need space for digits: 1,2,3,4,5,6,7,8,9,10,11,12. This gives us 9×1 + 3×2 = 15 characters total.
  2. sb: The space for the total number k repeated in each suffix. Since k appears in all k parts, this is len(str(k)) × k.
  3. sc: The space for symbols (<, /, >) in all parts, which is 3 × 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:

  1. sa - Sum of all part indices: We accumulate the total length of all part numbers from 1 to k. This is done incrementally using sa += len(str(k)). For example, when k=10, sa includes the lengths of "1", "2", ..., "9" (9 characters) plus "10" (2 characters).

  2. sb - Total length of k repeated: Since the total number k appears in every suffix, we calculate sb = len(str(k)) × k. For k=10, each suffix contains "10", so we need 2 × 10 = 20 characters for all the "10"s.

  3. sc - Fixed symbols length: Every suffix has exactly 3 symbols (<, /, >), so for k 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:

  1. Initialize an empty result array and a pointer i = 0 to track our position in the message
  2. For each part j from 1 to k:
    • 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

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 Evaluator

Example 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:

  1. Part 1: suffix <1/8> (5 chars) → can fit 1 char → "h<1/8>"
  2. Part 2: suffix <2/8> (5 chars) → can fit 1 char → "i<2/8>"
  3. Part 3: suffix <3/8> (5 chars) → can fit 1 char → " <3/8>"
  4. Part 4: suffix <4/8> (5 chars) → can fit 1 char → "t<4/8>"
  5. Part 5: suffix <5/8> (5 chars) → can fit 1 char → "h<5/8>"
  6. Part 6: suffix <6/8> (5 chars) → can fit 1 char → "e<6/8>"
  7. Part 7: suffix <7/8> (5 chars) → can fit 1 char → "r<7/8>"
  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, contributing O(n) iterations
  • Inside each iteration, we compute len(str(k)) multiple times, which takes O(log k) time since the number of digits in k is proportional to log k
  • When we find a valid k, we construct the answer by iterating through k parts, where each part involves string slicing and concatenation operations
  • The string slicing message[i : i + limit - len(tail)] takes O(limit) time, and since limit is bounded by n, this is O(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 in k, which is O(log n)
  • Since O(log n) is considered negligible compared to the input size, the space complexity excluding the output is effectively O(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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which type of traversal does breadth first search do?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More