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:
- Start with the first character from
word1
- Then take the first character from
word2
- Continue alternating between characters from each string
- 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"
andword2 = "xyz"
, the merged result would be"axbycz"
(alternating perfectly since both have equal length) - If
word1 = "ab"
andword2 = "pqrs"
, the merged result would be"apbqrs"
(after alternatinga
withp
andb
withq
, the remaining"rs"
fromword2
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.
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:
-
Using
zip_longest
for pairing: The functionzip_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
''
- For positions where both strings have characters, it creates pairs like
-
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
''
)
- Takes each pair
-
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 EvaluatorExample 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.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview 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
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 assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!