Check If Two String Arrays are Equivalent: Pointer Technique
Given two string arrays word1 and word2, return true if they represent the same string when each array's elements are concatenated in order. For example, word1 = ["ab", "c"] and word2 = ["a", "bc"] both form "abc", so the answer is true.
This is LeetCode 1662: Check If Two String Arrays are Equivalent, and while it has a clean one-liner solution, the four-pointer approach is far more instructive.
Why this problem matters
The one-liner join-and-compare solution works perfectly and is what you would write in production. But the four-pointer approach teaches something deeper: how to compare two logical sequences character by character without materializing them into a single string. This same technique appears in streaming algorithms, file comparison tools, and any scenario where you cannot afford to hold the full concatenated result in memory. Learning to traverse across array boundaries with pointer pairs is a building block that transfers to harder problems.
The key insight
You can compare the concatenated result character by character without actually concatenating. Maintain two pairs of pointers: (i, j) for word1, where i is the index of the current word and j is the character position within that word, and (k, l) for word2 with the same meaning. At each step, compare word1[i][j] with word2[k][l]. If they differ, return false. If they match, advance both character pointers. When a character pointer reaches the end of its current word, move to the next word and reset the character pointer to 0. If both array pointers run out at the same time, the arrays are equivalent.
The solution
def arrayStringsAreEqual(word1: list[str], word2: list[str]) -> bool:
i = j = k = l = 0
while i < len(word1) and k < len(word2):
if word1[i][j] != word2[k][l]:
return False
j += 1
if j == len(word1[i]):
i += 1
j = 0
l += 1
if l == len(word2[k]):
k += 1
l = 0
return i == len(word1) and k == len(word2)
The function uses four variables. i and j track which word and which character within that word we are looking at in word1. k and l do the same for word2. Each iteration compares one character from each side. After comparing, we advance the character pointer and, if it overflows the current word, roll over to the next word. The final check ensures both arrays were fully consumed. If one ran out before the other, the concatenated strings have different lengths and cannot be equal.
Also here is the one-liner for contrast:
def arrayStringsAreEqual(word1: list[str], word2: list[str]) -> bool:
return "".join(word1) == "".join(word2)
The join approach is simpler and perfectly fine for this problem's constraints. Use it in production code or when space is not a concern. Use the four-pointer approach when you want O(1) space, when you are working with streams, or when you want to practice the pointer-pair traversal technique that shows up in harder problems.
The four-pointer technique for crossing array boundaries generalizes beyond this problem. Whenever you have a sequence split across multiple chunks (file buffers, network packets, array-of-arrays), the same pattern of an outer index tracking which chunk and an inner index tracking the position within that chunk lets you process the logical sequence without flattening it first.
Visual walkthrough
Step 1: Initialize four pointers at the start
i=0, j=0 points to word1[0][0] = 'a'. k=0, l=0 points to word2[0][0] = 'a'. They match. Advance j and l.
Step 2: Advance character pointers within each word
word1[0][1] = 'b'. But l=1 is past word2[0] (length 1), so we move to k=1, l=0. Wait, let's check: word2[0] = 'a' has length 1, so l=1 means we crossed. We set k=1, l=0. Now word2[1][0] = 'b'. Match!
Step 3: Cross array boundary in word2
l reached the end of word2[0], so we advanced to k=1, l=0. Now word1[0][1] = 'b' and word2[1][0] = 'b'. They match. Advance both character pointers.
Step 4: Cross array boundary in word1
j reached the end of word1[0], so we advanced to i=1, j=0. Now word1[1][0] = 'c' and word2[1][1] = 'c'. They match. Advance both character pointers.
Step 5: Both arrays exhausted simultaneously
Both pointers have moved past the last word in their respective arrays (i=2, k=2). Since both were exhausted at the same time, the strings are equivalent. Return true.
The walkthrough shows how the pointer pairs independently cross word boundaries. When j reaches the end of word1[0], it resets to 0 and i advances to 1. The same happens for k and l on the other side. The two pairs do not need to cross boundaries at the same time, which is exactly why this technique works even when the words are sliced differently between the two arrays.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Join and compare | O(n) | O(n) |
| Four pointers | O(n) | O(1) |
Here n is the total number of characters across all words.
Time: Both approaches visit every character exactly once. The join approach does so during string concatenation and then during comparison. The four-pointer approach does it in a single pass. Both are O(n).
Space: The join approach creates two new strings of total length n, so it uses O(n) extra space. The four-pointer approach uses only four integer variables regardless of input size, so it uses O(1) extra space.
The building blocks
1. Four-pointer traversal across array boundaries
The core pattern is maintaining a (word index, char index) pair and rolling over when the char index overflows:
j += 1
if j == len(word1[i]):
i += 1
j = 0
This is the same idea as carrying digits in addition or advancing to the next node in a linked list. You work at the current level until you exhaust it, then move up.
2. Streaming character comparison
Once you can traverse both arrays one character at a time, comparing is trivial:
if word1[i][j] != word2[k][l]:
return False
The power of the streaming approach is that you can short-circuit as soon as you find a mismatch. The join approach must build both full strings before it can compare anything. For very long inputs where mismatches happen early, the pointer approach saves real time.
Edge cases
- Single-word arrays:
word1 = ["abc"]andword2 = ["abc"]. Degenerates to a regular string comparison. The pointers never cross a word boundary. - Single-character words:
word1 = ["a", "b", "c"]andword2 = ["a", "b", "c"]. Every character triggers a boundary crossing. Tests that the rollover logic works on every step. - Different total lengths:
word1 = ["ab"]andword2 = ["a"]. One array exhausts before the other. The final checki == len(word1) and k == len(word2)catches this. - Empty strings in the array:
word1 = ["", "abc"]andword2 = ["abc"]. The empty string has length 0, soj == len(word1[0])is immediately true, causing an instant rollover to the next word. - Both arrays empty:
word1 = []andword2 = []. The while loop never runs and bothiandkare 0, equal to their array lengths. Returnstrue. - Mismatch at the very last character:
word1 = ["ab", "c"]andword2 = ["ab", "d"]. The pointers agree for two characters before finally diverging. Tests that we compare all the way through rather than stopping early.
From understanding to recall
You now understand the four-pointer technique and why it works. But in an interview, understanding is not enough. You need to write the rollover logic without hesitation. The details that trip people up: remembering to check the boundary condition after incrementing (not before), resetting the character index to 0 when rolling over, and the final exhaustion check at the end.
Spaced repetition drills these details into muscle memory. You write the four-pointer loop from scratch at increasing intervals. After a few rounds, the boundary rollover pattern becomes automatic. You do not need to think through it step by step. You just type it.
Related posts
- Add Strings - Character-by-character processing across string boundaries
- Compare Version Numbers - Multi-pointer string comparison with logical segments
- Isomorphic Strings - Another character-by-character string comparison pattern
CodeBricks breaks Check If Two String Arrays are Equivalent into its pointer traversal and boundary rollover building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a similar streaming comparison problem shows up in your interview, you do not hesitate. You just write it.