Facebook Pixel

1768. Merge Strings Alternately

Problem Description

You need to merge two strings by combining their characters in an alternating pattern.

Given two strings word1 and word2, you should:

  1. Start with the first character from word1
  2. Then take the first character from word2
  3. Continue alternating between characters from each string
  4. If one string runs out of characters before the other, append all remaining characters from the longer string to the end

For example:

  • If word1 = "abc" and word2 = "xyz", the merged result would be "axbycz" (alternating perfectly since both have equal length)
  • If word1 = "ab" and word2 = "pqrs", the merged result would be "apbqrs" (after alternating a with p and b with q, the remaining "rs" from word2 is appended)

The solution uses zip_longest from Python's itertools module to pair up characters from both strings. When one string is shorter, fillvalue='' ensures empty strings are used as placeholders, allowing the longer string's remaining characters to be included naturally. The pairs are then joined together to form the final merged string.

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

Intuition

The key insight is recognizing this as a pairing problem. When we need to alternate between two sequences, we naturally think about processing them together in pairs - take one from the first, one from the second, and repeat.

The challenge comes when the strings have different lengths. We could handle this with index tracking and conditional checks, but there's a more elegant pattern: treat it as zipping two sequences together where missing elements are replaced with empty strings.

Think of it like merging two lines of people into a single line by alternating - person from line A, person from line B, and so on. When one line runs out, the remaining people from the longer line just walk straight through.

This leads us to zip_longest which pairs up elements from both strings position by position: (word1[0], word2[0]), (word1[1], word2[1]), and so on. When the shorter string ends, fillvalue='' provides empty strings as placeholders, so characters from the longer string still get paired and included.

Each pair (a, b) becomes a + b in the result. If b is an empty string (because word2 ended), we get just a. If a is empty (because word1 ended), we get just b. This naturally handles both the alternating pattern and the "append remaining characters" requirement in one unified approach.

The beauty of this solution is that it transforms a problem with special cases (different string lengths) into a uniform operation where every position is handled the same way.

Learn more about Two Pointers patterns.

Solution Approach

The solution uses direct simulation with Python's zip_longest function to elegantly handle the alternating merge pattern.

Implementation walkthrough:

  1. Using zip_longest for pairing: The function zip_longest(word1, word2, fillvalue='') creates an iterator that pairs up characters from both strings position by position:

    • For positions where both strings have characters, it creates pairs like ('a', 'p'), ('b', 'q')
    • When one string is exhausted, it continues pairing the remaining characters with empty strings ''
  2. Generator expression for merging: The expression a + b for a, b in zip_longest(...) processes each pair:

    • Takes each pair (a, b) from the zipped result
    • Concatenates them as a + b
    • This naturally handles all cases: when both characters exist, when only one exists (the other being '')
  3. Joining the result: ''.join(...) combines all the concatenated pairs into the final string.

Example trace with word1 = "ab" and word2 = "pqrs":

  • zip_longest produces: [('a', 'p'), ('b', 'q'), ('', 'r'), ('', 's')]
  • Generator yields: ['ap', 'bq', 'r', 's']
  • join produces: "apbqrs"

The time complexity is O(m + n) where m and n are the lengths of the two strings, as we process each character exactly once. The space complexity is O(m + n) for storing the result string.

This approach avoids explicit index management and conditional checks for different string lengths, making the code both concise and efficient.

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 trace through the solution with word1 = "ace" and word2 = "bd":

Step 1: Create character pairs using zip_longest

  • Position 0: 'a' from word1, 'b' from word2 β†’ pair ('a', 'b')
  • Position 1: 'c' from word1, 'd' from word2 β†’ pair ('c', 'd')
  • Position 2: 'e' from word1, nothing from word2 β†’ pair ('e', '')

Result: [('a', 'b'), ('c', 'd'), ('e', '')]

Step 2: Merge each pair

  • Pair ('a', 'b') β†’ 'a' + 'b' = 'ab'
  • Pair ('c', 'd') β†’ 'c' + 'd' = 'cd'
  • Pair ('e', '') β†’ 'e' + '' = 'e'

Result: ['ab', 'cd', 'e']

Step 3: Join all merged pairs

  • ''.join(['ab', 'cd', 'e']) = 'abcde'

Final result: "abcde"

The characters alternate perfectly: a(word1) β†’ b(word2) β†’ c(word1) β†’ d(word2) β†’ e(word1). When word2 ran out of characters after 'd', the remaining 'e' from word1 was naturally appended through the empty string pairing mechanism.

Solution Implementation

1from itertools import zip_longest
2
3class Solution:
4    def mergeAlternately(self, word1: str, word2: str) -> str:
5        """
6        Merge two strings alternately, taking one character from each string in turn.
7        If one string is longer, append the remaining characters at the end.
8      
9        Args:
10            word1: First string to merge
11            word2: Second string to merge
12          
13        Returns:
14            Merged string with characters alternating from word1 and word2
15        """
16        # Use zip_longest to pair characters from both strings
17        # fillvalue='' ensures shorter string is padded with empty strings
18        # Join each pair (a + b) to create alternating pattern
19        return ''.join(a + b for a, b in zip_longest(word1, word2, fillvalue=''))
20
1class Solution {
2    /**
3     * Merges two strings by alternating characters from each string.
4     * Takes one character from word1, then one from word2, and repeats.
5     * If one string is longer, appends remaining characters at the end.
6     * 
7     * @param word1 First string to merge
8     * @param word2 Second string to merge
9     * @return Merged string with alternating characters
10     */
11    public String mergeAlternately(String word1, String word2) {
12        // Get lengths of both input strings
13        int word1Length = word1.length();
14        int word2Length = word2.length();
15      
16        // StringBuilder for efficient string concatenation
17        StringBuilder mergedResult = new StringBuilder();
18      
19        // Iterate until we've processed all characters from both strings
20        for (int index = 0; index < word1Length || index < word2Length; index++) {
21            // Append character from word1 if index is within bounds
22            if (index < word1Length) {
23                mergedResult.append(word1.charAt(index));
24            }
25          
26            // Append character from word2 if index is within bounds
27            if (index < word2Length) {
28                mergedResult.append(word2.charAt(index));
29            }
30        }
31      
32        // Convert StringBuilder to String and return
33        return mergedResult.toString();
34    }
35}
36
1class Solution {
2public:
3    string mergeAlternately(string word1, string word2) {
4        // Get the lengths of both input strings
5        int length1 = word1.size();
6        int length2 = word2.size();
7      
8        // Initialize the result string to store merged characters
9        string result;
10      
11        // Iterate through both strings simultaneously
12        // Continue until all characters from both strings are processed
13        for (int i = 0; i < length1 || i < length2; ++i) {
14            // Add character from word1 if index is within bounds
15            if (i < length1) {
16                result += word1[i];
17            }
18          
19            // Add character from word2 if index is within bounds
20            if (i < length2) {
21                result += word2[i];
22            }
23        }
24      
25        // Return the merged string with alternating characters
26        return result;
27    }
28};
29
1/**
2 * Merges two strings alternately by taking characters from each string in turn
3 * @param word1 - First string to merge
4 * @param word2 - Second string to merge
5 * @returns A new string with characters alternating from word1 and word2
6 */
7function mergeAlternately(word1: string, word2: string): string {
8    // Array to store the merged characters
9    const mergedCharacters: string[] = [];
10  
11    // Get the lengths of both input strings
12    const word1Length: number = word1.length;
13    const word2Length: number = word2.length;
14  
15    // Iterate through both strings simultaneously
16    for (let index: number = 0; index < word1Length || index < word2Length; index++) {
17        // Add character from word1 if it exists at current index
18        if (index < word1Length) {
19            mergedCharacters.push(word1[index]);
20        }
21      
22        // Add character from word2 if it exists at current index
23        if (index < word2Length) {
24            mergedCharacters.push(word2[index]);
25        }
26    }
27  
28    // Join all characters into a single string and return
29    return mergedCharacters.join('');
30}
31

Time and Space Complexity

The time complexity is O(m + n), where m and n are the lengths of word1 and word2 respectively. The zip_longest function iterates through both strings once, processing each character exactly once. The join operation also traverses the resulting pairs once, contributing to the linear time complexity.

The space complexity is O(m + n) for the output string that stores the merged result. However, if we exclude the space required for the output (as mentioned in the reference answer), the auxiliary space complexity is O(1). The zip_longest creates an iterator that generates pairs on-the-fly without storing all pairs in memory, and the intermediate concatenation a + b uses constant extra space.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Manual Index Manipulation Errors

Many developers attempt to solve this problem using manual index tracking, which often leads to off-by-one errors or incorrect handling of remaining characters.

Problematic approach:

def mergeAlternately(self, word1: str, word2: str) -> str:
    result = []
    i = 0
    # Bug-prone: Easy to mess up the loop conditions
    while i < len(word1) or i < len(word2):
        if i < len(word1):
            result.append(word1[i])
        if i < len(word2):
            result.append(word2[i])
        i += 1
    return ''.join(result)

Why it's problematic: Manual index management increases complexity and the chance of boundary condition errors.

2. Incorrect String Concatenation in Loops

Using string concatenation with += in a loop creates unnecessary intermediate strings, leading to poor performance.

Inefficient approach:

def mergeAlternately(self, word1: str, word2: str) -> str:
    result = ""
    for i in range(max(len(word1), len(word2))):
        if i < len(word1):
            result += word1[i]  # Creates new string object each time
        if i < len(word2):
            result += word2[i]  # Creates another new string object
    return result

Better solution: Use a list to collect characters and join at the end, or use the zip_longest approach shown in the original solution.

3. Forgetting to Handle Empty Strings

Not considering edge cases where one or both input strings might be empty.

Incomplete solution:

def mergeAlternately(self, word1: str, word2: str) -> str:
    # Might fail or behave unexpectedly with empty strings
    min_len = min(len(word1), len(word2))
    result = []
    for i in range(min_len):
        result.append(word1[i] + word2[i])
    # Forgetting to handle remaining characters properly
    return ''.join(result) + word1[min_len:] + word2[min_len:]

Why it's problematic: While this specific implementation works, it's easy to introduce bugs when handling the remaining characters separately.

4. Using zip Instead of zip_longest

Using regular zip truncates to the shorter string's length, losing characters from the longer string.

Wrong approach:

def mergeAlternately(self, word1: str, word2: str) -> str:
    # This loses characters from the longer string!
    return ''.join(a + b for a, b in zip(word1, word2))

Example failure: With word1 = "ab" and word2 = "pqrs", this would return "apbq" instead of "apbqrs", missing the "rs" suffix.

Solution: Always use zip_longest with fillvalue='' for this type of problem to ensure all characters are included.

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

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Recommended Readings

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

Load More