Merge Strings Alternately: Two-Pointer Interleaving
Merge Strings Alternately is LeetCode 1768. You are given two strings word1 and word2. Merge them by adding characters in alternating order, starting with word1. If one string is longer than the other, append the remaining characters of the longer string to the end of the merged result.
Example: word1 = "abc", word2 = "pqr" returns "apbqcr". Another: word1 = "ab", word2 = "pqrs" returns "apbqrs". The first two characters of each string interleave, then the leftover "rs" from word2 gets tacked on at the end.
Why this problem matters
Merge Strings Alternately is one of the simplest two-pointer problems you will encounter, but it teaches a pattern that appears everywhere: walking through two sequences simultaneously with a shared index. You will see this same structure in problems that merge sorted arrays, interleave linked lists, and zip together parallel data.
It is also a good warm-up for understanding how to handle sequences of different lengths. The tricky part is not the interleaving itself. It is making sure you do not crash or skip characters when one string runs out before the other. The solution below handles that cleanly with a single loop and two conditional checks.
The key insight
You do not need separate pointers for each string. A single index i works for both. At each step, if i is still within bounds for word1, append word1[i]. If i is still within bounds for word2, append word2[i]. Then increment i. The loop runs as long as i is valid for at least one of the two strings.
This means you never need to handle the "leftover characters" as a special case. The same loop body naturally appends from whichever string still has characters remaining. When one string is exhausted, its if check simply stops firing, and the other string's characters flow into the result one by one.
The solution
def merge_alternately(word1: str, word2: str) -> str:
result = []
i = 0
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)
The loop condition while i < len(word1) or i < len(word2) keeps going until both strings are fully consumed. Inside, each if guard checks whether the current index is still valid for that string. When word1 is shorter, its guard starts failing first, and only word2 characters get appended for the remaining iterations.
Building the result as a list and joining at the end is the standard Python idiom for string construction. Repeated string concatenation with += creates a new string object every time, which makes it O(n^2) in the worst case. Appending to a list and calling "".join() once is O(n).
You could also solve this with zip_longest from itertools, but the explicit loop is clearer in an interview setting. It shows you understand the mechanics rather than relying on a library function. Interviewers want to see that you can handle the unequal-length case yourself.
Visual walkthrough
Here is the merge of word1 = "ab" and word2 = "pqrs", which produces "apbqrs". This example uses strings of different lengths so you can see how the loop handles the leftover characters from word2 once word1 is exhausted.
Step 1: i = 0. Take word1[0] = "a", then word2[0] = "p".
Both strings have a character at index 0. Append "a" from word1, then "p" from word2.
Step 2: i = 1. Take word1[1] = "b", then word2[1] = "q".
Both strings still have a character at index 1. Append "b" from word1, then "q" from word2.
Step 3: i = 2. word1 is exhausted. Take word2[2] = "r".
i = 2 is past the end of word1 (length 2), so we skip it. word2[2] = "r" is appended.
Step 4: i = 3. word1 is exhausted. Take word2[3] = "s".
word1 is still exhausted. Append word2[3] = "s". Both strings are now fully consumed. Result: "apbqrs".
Notice how the loop body never changes. The same two if checks run every iteration. When word1 runs out at i = 2, the first if simply stops appending, and the second if keeps pulling from word2. No special "append the rest" step is needed.
Complexity analysis
| Aspect | Value |
|---|---|
| Time | O(m + n) |
| Space | O(m + n) |
| Difficulty | Easy |
Time: O(m + n) where m and n are the lengths of word1 and word2. The loop runs max(m, n) times, and each iteration does O(1) work. Every character from both strings is visited exactly once.
Space: O(m + n) for the result list. The merged string contains every character from both inputs, so its length is always m + n.
The building blocks
Single-index parallel traversal
The core technique here is using one index to walk through two sequences at the same time. Instead of maintaining separate pointers i and j that advance independently, you use a shared i and let boundary checks decide which sequences contribute at each step. This works whenever the two sequences are consumed at the same rate, one element per iteration.
You will see this pattern again in problems like Zigzag Conversion, where characters from a single string are distributed across multiple rows in a cyclic pattern. The shared index advances through the input while conditional logic decides where each character goes.
Guarded append with boundary checks
The if i < len(word1) pattern is a building block in its own right. Rather than splitting the problem into phases ("first interleave, then append the remainder"), you write a single loop with guards. The guards naturally deactivate when a sequence is exhausted. This eliminates an entire class of off-by-one errors that come from trying to figure out where one phase ends and the next begins.
The same guarded-append idea shows up in merge operations on sorted arrays, where you pull from whichever side has the smaller element and let the boundary check handle the case where one side runs out first.
Edge cases
- Equal lengths.
word1 = "abc",word2 = "def". The result is"adbecf". Both strings run out on the same iteration. No leftover characters. - One string is empty.
word1 = "",word2 = "xyz". The loop runs three times. Theword1guard never fires. The result is just"xyz". - Both strings are empty.
word1 = "",word2 = "". The loop condition is immediately false. The result is"". - One string is much longer.
word1 = "a",word2 = "bcdef". They interleave for one character each ("ab"), then the remaining"cdef"fromword2is appended one character per iteration. - Single character each.
word1 = "x",word2 = "y". The result is"xy". The simplest non-trivial case.
From understanding to recall
This problem is easy to understand on first read. The code is short, the logic is clean, and there are no tricky edge cases to worry about. But that simplicity is exactly what makes it easy to forget. You read it, nod, and move on. Then in an interview, you hesitate: was it while i < len(word1) and ... or while i < len(word1) or ...? Do you need a separate loop for the remaining characters?
The or in the loop condition is the detail that holds everything together. Using and would stop the loop the moment either string runs out, leaving characters on the table. Using or keeps going until both are done. That one keyword is the difference between a correct solution and a bug.
Spaced repetition helps you lock in these small but critical details. Write the solution from memory today. Try again in three days. Then a week later. After a few reps, the loop structure and the or condition become automatic.
Related posts
- Add Strings - Another problem where you walk two strings in parallel with a shared loop, handling different lengths gracefully
- Zigzag Conversion - Distributes characters from one string across multiple rows using cyclic index logic
- Reverse String - The simplest two-pointer string manipulation, a good companion drill for building pointer intuition
CodeBricks helps you move from understanding a solution to actually retaining it. Each problem is broken into reusable building blocks, and spaced repetition schedules reviews so you practice recalling them at the right intervals. Instead of re-reading solutions you have already forgotten, you actively reconstruct them, which is what builds lasting recall.