2796. Repeat String


Problem Description

The problem requires us to enhance the String prototype in TypeScript by adding a new method called replicate. This new method should take an integer x and return a new string where the original string is repeated x number of times. A key point in the description is that we should implement this functionality without using the built-in string.repeat method provided in JavaScript/TypeScript.

Intuition

To arrive at a solution for replicating a string x times without using the string.repeat method, we can utilize an array as an intermediary. The intuition is based on two steps:

  1. Create an array with x number of elements, with each element being the original string that we want to replicate. This is done using the new Array(times) constructor which creates an array of the specified length, and fill(this) which fills it with the current string context (this refers to the string instance where the method is called).

  2. Join all the array elements into a single string. The join('') method is used to concatenate all the elements of the array into a single string, with the empty string '' serving as the separator (which means there will be no characters between the repetitions of the original string).

When we put these two steps together in the replicate function, we get a new string consisting of the original string repeated x times without using the string.repeat method.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Depth first search can be used to find whether two components in a graph are connected.

Solution Approach

The implementation of the replicate method in TypeScript involves extending the String prototype, which is a standard JavaScript object prototype pattern that allows us to add new properties or methods to all instances of the String class.

Here are the steps taken in the implementation:

  1. Extending the String Interface: To add a new method to the String prototype in TypeScript, we first need to extend the String interface with our new method signature. This is done through declaration merging by declaring an expanded interface named String globally which includes our custom method replicate(times: number): string;.

    1declare global {
    2    interface String {
    3        replicate(times: number): string;
    4    }
    5}

    This tells TypeScript that every String object will now have a method called replicate that takes a number and returns a string.

  2. Implementing the replicate Method: The replicate method is then added to the String prototype.

    1String.prototype.replicate = function (times: number) {
    2    return new Array(times).fill(this).join('');
    3};
    • Algorithm: The algorithm employed here is straightforward and involves the creation of an array followed by joining its elements. No complex patterns or data structures are required.

    • Creating an Array of Repetitions: We use the new Array(times) constructor to create an array of length equal to the number of times the string needs to be replicated. Then, the fill method fills up the array with the original string (this) in all positions. This effectively creates an array where each element is the string to be replicated.

    • Joining the Array: Once the array is filled, we use the join method with an empty string as a separator to concatenate all the elements of the array, effectively repeating the original string times number of times.

This solution is efficient because the heavy lifting is done by native array operations, which are highly optimized in JavaScript engines. Since we are avoiding recursion and repeated string concatenation, we minimize the risk of performance issues and potential call stack overflows for large values of times. It is a simple and elegant way to add custom repeat functionality to strings in TypeScript.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What is the running time of the following code?

1int sqrt(int n) {
2  for (int guess = 1; guess * guess <= n; guess++) {
3    if (guess * guess == n) {
4      return guess;
5    }
6  }
7  return -1;
8}

Example Walkthrough

Let's walk through a small example to illustrate the solution approach for enhancing the String prototype with a replicate method in TypeScript.

Suppose we have the string "hello" and we want to replicate it 3 times. To achieve this using our custom replicate method, we would start by calling "hello".replicate(3).

Here's what happens step by step when we call the replicate method:

  1. An array with 3 elements is created using new Array(3).

  2. This array is then filled with the string "hello" in all positions, making the array look like this: ["hello", "hello", "hello"].

  3. Finally, we join all the array elements with no separator using join(''), which results in the string "hellohellohello".

So, if we execute the following TypeScript code:

1"hello".replicate(3)

The output we expect to see would be:

1"hellohellohello"

With this example, we have shown how the replicate method uses the concept of arrays to create multiple copies of a string and concatenate them to produce a final string with the original string repeated the specified number of times without using the built-in string.repeat method.

Solution Implementation

1# Extends the built-in string class to add a new method called 'replicate'
2class ExtendedString(str):
3    # The 'replicate' method takes a number and returns a string where the original
4    # string is repeated that many times.
5    def replicate(self, times):
6        # Return the current string instance multiplied by 'times', which effectively
7        # repeats the string 'times' times.
8        return self * times
9
10# To ensure the 'replicate' method is available to all string instances,
11# extend the global `str` type by subclassing ExtendedString.
12# This allows the usage of 'replicate' on any string object created after this point.
13str = ExtendedString
14
15# Example of using the newly added 'replicate' method
16example_string = str("Hello ")
17replicated_string = example_string.replicate(3)
18print(replicated_string)  # Output: Hello Hello Hello 
19```
20
21Please note, in Python, monkey patching built-in types (like adding a method to the built-in `str` type) is not a common or recommended practice because it can lead to unexpected behaviors, especially in larger projects or with third-party libraries. The example above demonstrates how to add the method by subclassing, but whenever you create a string, you would now have to explicitly use `str()` to ensure it has the `replicate` method, which is not the case in default strings directly created with quotes.
22
23For a simpler, more Pythonic solution that doesn't attempt to modify the built-in `str` type:
24
25```python
26# Define a function called 'replicate' that replicates a string.
27def replicate(string, times):
28    # Return the string repeated 'times' times.
29    return string * times
30
31# Example usage of the 'replicate' function
32example_string = "Hello "
33replicated_string = replicate(example_string, 3)
34print(replicated_string)  # Output: Hello Hello Hello
35
1public final class StringUtils {
2
3    /**
4     * Replicates a given String a specified number of times.
5     *
6     * @param input The string to be replicated.
7     * @param times The number of times to replicate the string.
8     * @return A new String that is a replication of the input String 'times' times.
9     */
10    public static String replicate(String input, int times) {
11        // Use a StringBuilder to efficiently replicate the string.
12        StringBuilder builder = new StringBuilder();
13      
14        // Loop the number of times required, appending the input each time.
15        for (int i = 0; i < times; i++) {
16            builder.append(input);
17        }
18      
19        // Return the result as a string.
20        return builder.toString();
21    }
22
23    // Private constructor to prevent instantiation.
24    // Utility classes should not be instantiated.
25    private StringUtils() {
26    }
27  
28    // main method for example usage
29    public static void main(String[] args) {
30        // Example usage of the replicate method
31        String originalString = "abc";
32        String replicatedString = StringUtils.replicate(originalString, 3);
33        System.out.println(replicatedString); // Expected output: "abcabcabc"
34    }
35}
36
1#include <string> // Include the standard string library
2#include <iostream> // Include the IO stream library for demonstration purposes
3
4// Define the 'replicate' function that takes a string and an int
5// It returns a new string where the original string is repeated 'times' times
6std::string replicate(const std::string& str, int times) {
7    std::string result; // Initialize the result string
8    // Reserve enough space in result to avoid repeatedly reallocating
9    result.reserve(str.size() * times);
10    for(int i = 0; i < times; ++i) {
11        result += str; // Append the original string 'times' times
12    }
13    return result; // Return the result string
14}
15
16// Demonstration of the 'replicate' function
17int main() {
18    std::string original = "Hello";
19    int times = 3;
20    std::string replicated = replicate(original, times); // Use the 'replicate' function
21    std::cout << replicated; // Outputs: HelloHelloHello
22    return 0; // Return successful exit code
23}
24
1// Extend the global String interface to add a new method called 'replicate'
2declare global {
3    // The 'replicate' method is expected to be present on all string instances
4    // It takes a number and returns a string where the original string is repeated that many times
5    interface String {
6        replicate(times: number): string;
7    }
8}
9
10// Define the replicate function on the String prototype, making it available to all strings
11String.prototype.replicate = function (times: number): string {
12    // Initialize an array of 'times' length, fill it with the current string instance (this),
13    // and then join all elements into a single string where the original string is replicated
14    return new Array(times + 1).join(this);
15};
16
17// Ensure the extended String interface is globally available
18export {};
19
Not Sure What to Study? Take the 2-min Quiz:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?

Time and Space Complexity

Time Complexity

The time complexity of the replicate method can be considered as O(n * m), where n is the number of times the string is replicated, and m is the length of the source string.

This is because the .fill method instantiates an array of size n and fills it with the source string. Then, the .join method iterates over this array to concatenate the strings together. If the array .fill method is implemented in a way that performs a deep copy, the time to fill the array is O(n), but as it copies references in this case, we can consider this part O(n) for the references. However, the .join operation requires concatenating the string n times, with each concatenation operation being proportional to the length of the string, which is m. Therefore, the .join operation dominates and results in a time complexity of O(n * m).

Space Complexity

The space complexity of the replicate function is O(n * m) as well. This is because it creates an array of n elements, each of which is a reference to the source string of length m. When concatenated, the resulting string occupies space proportional to n * m.

One could argue that since .fill is using the same reference, the array does not require additional space proportional to the length of the string. However, the resulting string from .join still requires n * m space. Therefore, the overall space complexity remains O(n * m).

Fast Track Your Learning with Our Quick Skills Quiz:

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


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫