2796. Repeat String

EasyJavaScript
Leetcode Link

Problem Description

The task is to extend the built-in String prototype in TypeScript/JavaScript by adding a custom method called replicate(x). This method should take a number x as an argument and return the original string repeated x times.

For example:

  • If you have the string "hello" and call "hello".replicate(3), it should return "hellohellohello"
  • If you have the string "ab" and call "ab".replicate(4), it should return "abababab"

The key requirement is that you cannot use the built-in string.repeat() method to implement this functionality. You need to find an alternative approach to achieve the string repetition.

The solution uses TypeScript's global declaration to extend the String interface, then implements the method by creating an array of the specified size, filling it with the original string, and joining all elements together with an empty string delimiter. This effectively concatenates the string times number of times without using the repeat() method.

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

Intuition

Since we cannot use the built-in repeat() method, we need to think of alternative ways to duplicate a string multiple times. The most straightforward approach would be to use a loop and concatenate the string to itself x times, but there's a more elegant solution using array manipulation.

The key insight is that we can leverage JavaScript's array methods to achieve repetition. When we create an array with a specific length and fill it with the same value, we essentially have that value repeated multiple times in memory. The challenge then becomes how to convert this array of repeated strings back into a single concatenated string.

This leads us to the pattern: new Array(times).fill(this).join('')

  1. new Array(times) creates an array with times empty slots
  2. .fill(this) populates each slot with the current string (where this refers to the string the method is called on)
  3. .join('') concatenates all array elements into a single string with no separator

For example, if we call "ab".replicate(3):

  • We create an array of length 3: [empty, empty, empty]
  • Fill it with "ab": ["ab", "ab", "ab"]
  • Join with empty separator: "ababab"

This approach is concise and avoids manual loop construction or recursive calls, making it both readable and efficient for the task at hand.

Solution Implementation

1# Extend the built-in str class to include the replicate method 2class ExtendedString(str): 3 """ 4 Extended string class that adds a replicate method to standard string functionality 5 """ 6 7 def replicate(self, times: int) -> str: 8 """ 9 Replicates the string a specified number of times 10 11 Args: 12 times: The number of times to replicate the string 13 14 Returns: 15 A new string with the original string repeated 16 """ 17 # Create a list with the specified length, fill it with the current string, 18 # and join all elements into a single string 19 return ''.join([self] * times) 20 21 22# Alternative approach: Monkey-patching the built-in str class 23# Note: This is generally not recommended in Python but matches the original intent 24def replicate(self, times: int) -> str: 25 """ 26 Replicates the string a specified number of times 27 28 Args: 29 times: The number of times to replicate the string 30 31 Returns: 32 A new string with the original string repeated 33 """ 34 # Multiply the string by the specified number of times 35 # Python's string multiplication is more idiomatic than array/list joining 36 return self * times 37 38 39# Attach the method to the str class 40# This modifies the built-in str type globally 41str.replicate = replicate 42
1/** 2 * StringReplicator class provides utility methods for string replication 3 * Since Java doesn't support prototype extension like TypeScript/JavaScript, 4 * we use a utility class with static methods 5 */ 6public class StringReplicator { 7 8 /** 9 * Replicates the string a specified number of times 10 * @param str - The string to be replicated 11 * @param times - The number of times to replicate the string 12 * @return A new string with the original string repeated 13 */ 14 public static String replicate(String str, int times) { 15 // Validate input parameters 16 if (str == null) { 17 return null; 18 } 19 20 if (times <= 0) { 21 return ""; 22 } 23 24 // Create a StringBuilder for efficient string concatenation 25 StringBuilder result = new StringBuilder(str.length() * times); 26 27 // Append the string the specified number of times 28 for (int i = 0; i < times; i++) { 29 result.append(str); 30 } 31 32 // Return the concatenated string 33 return result.toString(); 34 } 35 36 /** 37 * Alternative implementation using Java 11+ String.repeat() method 38 * @param str - The string to be replicated 39 * @param times - The number of times to replicate the string 40 * @return A new string with the original string repeated 41 */ 42 public static String replicateUsingRepeat(String str, int times) { 43 // Validate input parameters 44 if (str == null) { 45 return null; 46 } 47 48 if (times <= 0) { 49 return ""; 50 } 51 52 // Use built-in repeat method (available in Java 11+) 53 return str.repeat(times); 54 } 55} 56
1#include <string> 2#include <sstream> 3 4// Extension class to add replicate functionality to strings 5class StringExtensions { 6public: 7 /** 8 * Replicates a string a specified number of times 9 * @param str - The string to replicate 10 * @param times - The number of times to replicate the string 11 * @return A new string with the original string repeated 12 */ 13 static std::string replicate(const std::string& str, int times) { 14 // Handle edge cases 15 if (times <= 0) { 16 return ""; 17 } 18 19 // Create a string stream for efficient string concatenation 20 std::stringstream result; 21 22 // Append the string 'times' number of times 23 for (int i = 0; i < times; ++i) { 24 result << str; 25 } 26 27 // Return the concatenated string 28 return result.str(); 29 } 30}; 31 32// Alternative implementation using string reserve for better performance 33class StringUtils { 34public: 35 /** 36 * Replicates a string a specified number of times (optimized version) 37 * @param str - The string to replicate 38 * @param times - The number of times to replicate the string 39 * @return A new string with the original string repeated 40 */ 41 static std::string replicate(const std::string& str, int times) { 42 // Handle edge cases 43 if (times <= 0) { 44 return ""; 45 } 46 47 // Pre-allocate memory for better performance 48 std::string result; 49 result.reserve(str.length() * times); 50 51 // Append the string 'times' number of times 52 for (int i = 0; i < times; ++i) { 53 result += str; 54 } 55 56 return result; 57 } 58}; 59
1// Extend the global String interface to include the replicate method 2declare global { 3 interface String { 4 /** 5 * Replicates the string a specified number of times 6 * @param times - The number of times to replicate the string 7 * @returns A new string with the original string repeated 8 */ 9 replicate(times: number): string; 10 } 11} 12 13// Implementation of the replicate method for String prototype 14String.prototype.replicate = function (times: number): string { 15 // Create an array with the specified length, fill it with the current string, 16 // and join all elements into a single string 17 return new Array(times).fill(this.valueOf()).join(''); 18}; 19 20// Export empty object to make this file a module 21export {}; 22

Solution Approach

The implementation consists of two main parts: extending the TypeScript type system and implementing the actual method.

Step 1: Type Declaration

declare global {  interface String {  replicate(times: number): string;  } }

This TypeScript declaration extends the global String interface to include our new replicate method. This tells TypeScript that all strings will have this method available, preventing type errors when we call it.

Step 2: Method Implementation

String.prototype.replicate = function (times: number) {  return new Array(times).fill(this).join(''); };

The implementation breaks down into three operations:

  1. Array Creation: new Array(times) creates an array with a length equal to the times parameter. Initially, this array contains empty slots.

  2. Array Filling: .fill(this) populates every position in the array with the current string value. In the context of a prototype method, this refers to the string instance on which the method is called.

  3. String Concatenation: .join('') merges all array elements into a single string. By using an empty string '' as the separator, the strings are concatenated directly without any characters between them.

Example Walkthrough: When calling "cat".replicate(3):

  • new Array(3)[empty, empty, empty]
  • .fill("cat")["cat", "cat", "cat"]
  • .join('')"catcatcat"

This approach efficiently handles edge cases:

  • If times is 0, it creates an empty array, resulting in an empty string
  • If times is 1, it returns the original string
  • The method works for any positive integer value of times

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 the example "hi".replicate(4):

Step 1: Method Call When we call "hi".replicate(4), the method executes with this = "hi" and times = 4.

Step 2: Create Empty Array

new Array(4) // Creates: [empty, empty, empty, empty]

We now have an array with 4 empty slots.

Step 3: Fill Array with String

[empty, empty, empty, empty].fill("hi") // Results in: ["hi", "hi", "hi", "hi"]

Each empty slot is replaced with our original string "hi".

Step 4: Join Array Elements

["hi", "hi", "hi", "hi"].join('') // Results in: "hihihihi"

The join('') method concatenates all array elements with no separator between them.

Final Result: "hihihihi"

Edge Case Example: "test".replicate(0)

  • new Array(0) creates an empty array []
  • .fill("test") has nothing to fill, remains []
  • .join('') on an empty array returns ""
  • Result: empty string

This demonstrates how the solution elegantly handles both normal cases and edge cases without explicit conditional logic.

Time and Space Complexity

Time Complexity: O(n) where n is the value of the times parameter.

The time complexity breaks down as follows:

  • new Array(times) creates an array of size times - O(n)
  • .fill(this) iterates through the array to fill each element with the string reference - O(n)
  • .join('') iterates through the array to concatenate all elements into a single string - O(n)

Since these operations are sequential, the overall time complexity is O(n) + O(n) + O(n) = O(n).

Space Complexity: O(n * m) where n is the value of the times parameter and m is the length of the original string.

The space complexity analysis:

  • new Array(times) creates an array with times elements - O(n) space
  • .fill(this) populates the array with references to the string (not copies), so no additional space beyond the references
  • .join('') creates a new string of length times * m where m is the length of the original string - O(n * m) space
  • The intermediate array also holds n references - O(n) space

The dominant factor is the final string of size O(n * m), making the overall space complexity O(n * m).

Common Pitfalls

1. Not Handling Negative or Non-Integer Input

The implementation doesn't validate the times parameter. Passing negative numbers or non-integers can cause unexpected behavior:

  • In TypeScript: new Array(-1) throws a RangeError
  • In Python: Negative multiplication returns an empty string, which might not be the intended behavior

Solution:

String.prototype.replicate = function (times: number) {  // Validate input  if (times < 0 || !Number.isInteger(times)) {  throw new Error('Parameter must be a non-negative integer');  }  return new Array(times).fill(this).join(''); };
def replicate(self, times: int) -> str:  if not isinstance(times, int) or times < 0:  raise ValueError('Parameter must be a non-negative integer')  return self * times

2. Memory Overflow with Large Numbers

Creating very large arrays or strings can cause memory issues. For example, "hello".replicate(1000000000) would attempt to create an enormous string.

Solution: Add a reasonable upper limit:

String.prototype.replicate = function (times: number) {  const MAX_REPLICATIONS = 1000000; // Set reasonable limit  if (times > MAX_REPLICATIONS) {  throw new Error(`Cannot replicate more than ${MAX_REPLICATIONS} times`);  }  return new Array(times).fill(this).join(''); };

3. Type Safety Issues in TypeScript

Without proper type declaration, TypeScript will throw compilation errors when trying to use the replicate method. Forgetting the declare global block is a common mistake.

Solution: Always include the type declaration before implementation:

declare global {  interface String {  replicate(times: number): string;  } } export {}; // Make this file a module

4. Mutating Built-in Prototypes (Python)

Monkey-patching built-in types like str in Python can lead to:

  • Conflicts with third-party libraries
  • Unexpected behavior in other parts of the codebase
  • Difficulty in debugging

Solution: Use a wrapper class or utility function instead:

def replicate_string(s: str, times: int) -> str:  """Standalone function that doesn't modify built-in types"""  if not isinstance(times, int) or times < 0:  raise ValueError('Times must be a non-negative integer')  return s * times  # Or use a wrapper class class ReplicableString(str):  def replicate(self, times: int) -> str:  return self * times

5. Performance Issues with Array Join Approach

In TypeScript, creating an array and then joining it has overhead compared to simple string concatenation for small repetitions.

Solution: Use a more efficient approach for small values:

String.prototype.replicate = function (times: number) {  if (times <= 0) return '';  if (times === 1) return this.toString();   // For small numbers, use simple concatenation  if (times < 10) {  let result = '';  for (let i = 0; i < times; i++) {  result += this;  }  return result;  }   // For larger numbers, use array join  return new Array(times).fill(this).join(''); };
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

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

Load More