2138. Divide a String Into Groups of Size k
Problem Description
This problem is about partitioning a string s
into several groups of characters. Each group must contain exactly k
characters. The partitioning follows a set procedure:
- We start from the beginning of the string and create groups by sequentially taking
k
characters for each until we reach the end of the string. - If the remaining characters at the end of the string are fewer than
k
, we fill the last group with a fill characterfill
to make sure it also has exactlyk
characters.
The constraint is that after removing any fill
characters added to the last group and then concatenating all groups, we should get the original string s
.
The goal is to return an array of strings, where each element is one of the groups created by this procedure.
Intuition
The solution relies on a straightforward approach:
- Iteratively process the string in increments of
k
characters. For everyk
characters, we take a substring from the original strings
and add it to the result list. - We use a Python list comprehension to perform this operation concisely. The list comprehension iterates over the range of indices starting from 0 up to the length of the string
s
, skippingk
indices at a time. - For each iteration, we take a slice of the string
s
from the current indexi
up toi + k
. Ifi + k
exceeds the length of the strings
, Python's slice operation will just return the substring up to the end of the string, which would be less thank
characters. - To ensure that each substring in the result list has exactly
k
characters, we use the.ljust()
string method. This method pads the string on the right with thefill
character until the string reaches a specified length (k
in our case). If the substring already hask
characters,.ljust()
leaves it unchanged. - This solution is efficient and only goes through the string once. It also takes care of the edge case where the last group is shorter than
k
characters without needing additional conditional checks.
By leveraging Python's string slicing and the .ljust()
method, we achieve an elegant and efficient solution that directly constructs the desired array of group strings.
Solution Approach
In the given problem, we have a simple yet efficient solution that efficiently partitions the string into the required groups. The solution uses Python list comprehension, string slicing, and the str.ljust
method. Let's walk through the key parts of the implementation:
-
List Comprehension: This is a concise way to create lists in Python. It allows for the construction of a list by iterating over some sequence and applying an expression to each item in the sequence. In our case, we generate a list that contains each group of the partitioned string
s
. -
String Slicing: A vital feature of Python that allows us to extract a specific portion of a string using the syntax
string[start:end]
, wherestart
is the index of the first character inclusive andend
is the index of the last character exclusive. Ifend
extends beyond the length of the string, Python simply returns the substring up to the end without raising an error. -
str.ljust(k, fill)
Method: This string method left-justifies the string, usingfill
(a single character) as the fill character, until the whole string is of lengthk
. If the string is alreadyk
characters or longer, no filling occurs. This method is perfect for ensuring that each group is exactlyk
characters long, even the last group if it's originally shorter thank
characters.
The implementation is straightforward:
-
We iterate over the indices of the string
s
with therange()
function, starting from0
and ending atlen(s)
, taking steps ofk
at a time. This ensures that each iteration corresponds to a group ofk
characters. -
For each index
i
, we slice the strings
fromi
toi+k
. This gives us a substring ofs
which is at mostk
characters long. -
We apply the
ljust()
method to each substring with the specifiedfill
character to ensure it isk
characters long.
After iterating over all possible starting indices that are k
characters apart, we collect all the modified substrings into a list. This list represents the groups after the partitioning process and is the final output of the function.
The code for creating each group looks like this:
[s[i : i + k].ljust(k, fill) for i in range(0, len(s), k)]
As a result, we get a list of strings, each representing a group of s
that is exactly k
character long, where the last group is filled with fill
character if needed.
This approach is both simple to understand and effective at solving the problem, utilizing core Python string manipulation features to achieve the desired result with minimal code.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's assume our input string s
is "abcdefghi" and we are to partition this string into groups of k = 3
characters. We also have a fill character fill = "x"
to use if necessary.
According to the problem description, we would start at the beginning of the string and take groups of k
characters in sequence. Our groups would be "abc," "def," and "ghi." Since "ghi" has exactly 3 characters, there is no need to use the fill
character in this example.
We can illustrate the solution approach with the following steps:
-
Start at the beginning of the string, with
i = 0
. -
Take the substring from
i
toi + k
, which translates tos[0:3]
. This gives us "abc". -
Move to the next
k
characters, incrementi
byk
toi = 3
, and take the substrings[3:6]
, which gives us "def". -
Continue the process by setting
i
to 6 and taking the substrings[6:9]
, yielding "ghi".
We now have the strings "abc," "def," and "ghi". All of these are of length k
, so the ljust
method is not necessary in this case.
However, if we had a different string, let's say s
equals "abcdefg", with the same value for k
, we'd have an additional step for the last group:
-
Following the previous steps, we would get "abc" and "def".
-
The next
i
is 6. The substrings[6:9]
would result in "g". Since this group is less thank
characters, we need to pad it with thefill
character "x" to get "gxx".
Thus, the use of s[i : i + k].ljust(k, fill)
in our list comprehension is to handle cases like this, ensuring the last group always has k
characters. If the last group is originally shorter than k
characters, it gets padded with fill
until it's k
characters long.
So, the partitioned groups for "abcdefg" will be:
- "abc"
- "def"
- "gxx"
The list comprehension puts this all together elegantly, completing the task in a single line of code.
Solution Implementation
1# Solution class containing the method to divide and fill a string.
2class Solution:
3 def divideString(self, s: str, k: int, fill: str) -> list[str]:
4 # This method takes in a string `s`, a chunk size `k`, and a fill character `fill`.
5 # It divides the string `s` into chunks of size `k` and if the last chunk
6 # is smaller than `k`, it fills it with the `fill` character until it is of size `k`.
7 # The method returns a list of these chunks.
8
9 # Initialize an empty list to store the chunks of the string.
10 result = []
11
12 # Loop through the string in steps of `k` to get each chunk.
13 for i in range(0, len(s), k):
14 # Extract the chunk from the string starting at index `i` up to `i + k`.
15 chunk = s[i : i + k]
16
17 # If the length of the chunk is less than `k`, fill it with the fill character.
18 # The `ljust` method is used to fill the chunk with the given character until it reaches the desired length.
19 filled_chunk = chunk.ljust(k, fill)
20
21 # Append the filled chunk to the `result` list.
22 result.append(filled_chunk)
23
24 # Return the list of chunks.
25 return result
26
1class Solution {
2 public String[] divideString(String input, int partitionSize, char fillCharacter) {
3 // Determine the length of the input string.
4 int inputLength = input.length();
5
6 // Calculate the required number of partitions.
7 int totalPartitions = (inputLength + partitionSize - 1) / partitionSize;
8
9 // Initialize the answer array with the calculated size.
10 String[] partitions = new String[totalPartitions];
11
12 // If the input string is not a multiple of partition size, append fill characters to make it so.
13 if (inputLength % partitionSize != 0) {
14 input += String.valueOf(fillCharacter).repeat(partitionSize - inputLength % partitionSize);
15 }
16
17 // Loop through each partition, filling the partitions array with substrings of the correct size.
18 for (int i = 0; i < partitions.length; ++i) {
19 // Calculate the start and end indices for the substring.
20 int start = i * partitionSize;
21 int end = (i + 1) * partitionSize;
22
23 // Extract the substring for the current partition and assign it to the partitions array.
24 partitions[i] = input.substring(start, end);
25 }
26
27 // Return the final array of partitions.
28 return partitions;
29 }
30}
31
1class Solution {
2public:
3 // Function to divide a string into substrings of equal length k.
4 // If the length of string s is not a multiple of k, fill the last substring with the 'fill' char.
5 vector<string> divideString(string s, int k, char fill) {
6 int stringLength = s.size();
7 // If string length is not a multiple of k, append 'fill' characters
8 if (stringLength % k) {
9 for (int i = 0; i < k - stringLength % k; ++i) {
10 s.push_back(fill);
11 }
12 }
13 vector<string> substrings;
14 // Divide the string into substrings of length k
15 for (int i = 0; i < s.size() / k; ++i) {
16 substrings.push_back(s.substr(i * k, k));
17 }
18 return substrings; // Return the vector of substrings
19 }
20};
21
1// Declare an array to store the resulting substrings
2const substrings: string[] = [];
3
4// Function to divide a string into substrings of equal length `k`
5// If the length of string `s` is not a multiple of `k`, fill the last substring with the `fill` character
6function divideString(s: string, k: number, fill: string): string[] {
7 let stringLength: number = s.length;
8
9 // If string length is not a multiple of `k`, append `fill` characters
10 while (stringLength % k !== 0) {
11 s += fill;
12 stringLength++;
13 }
14
15 // Divide the string into substrings of length `k`
16 for (let i = 0; i < s.length; i += k) {
17 substrings.push(s.substring(i, i + k));
18 }
19
20 // Return the array of substrings
21 return substrings;
22}
23
Time and Space Complexity
Time Complexity:
The time complexity of the provided code is mainly determined by the list comprehension. It consists of a for
loop that runs every k
characters across the string s
and an ljust
operation within each iteration. Here, n
is the length of the input string s
.
- The
for
loop iteratesn/k
times because it jumpsk
characters in each iteration. - The
ljust
operation potentially runs inO(k)
time in the worst case, when the last segment of the string is less thank
characters and needs to be filled with the fill character.
Therefore, we consider all iterations to get the time complexity:
n/k
iterations * O(k)
per ljust
operation = O(n/k * k)
= O(n)
.
So, the overall time complexity of the code is O(n)
.
Space Complexity:
- The space complexity is determined by the space required to store the output list.
- Each element of the list is a string of
k
characters (or fewer iffill
characters are added), so the space taken by each element isO(k)
. - Since there are
n/k
elements in the list, the total space for the list isO(n/k * k)
=O(n)
.
Additionally, the space taken by the input string s
does not count towards the space complexity, as it is considered given and not part of the space used by the algorithm.
Thus, the overall space complexity of the code is O(n)
, where n
is the length of the input string s
.
Learn more about how to find time and space complexity quickly using problem constraints.
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!