190 lines
8.8 KiB
YAML
190 lines
8.8 KiB
YAML
title: Check If Two String Arrays are Equivalent
|
|
slug: check-if-two-string-arrays-are-equivalent
|
|
difficulty: easy
|
|
leetcode_id: 1662
|
|
leetcode_url: https://leetcode.com/problems/check-if-two-string-arrays-are-equivalent/
|
|
categories:
|
|
- arrays
|
|
- strings
|
|
patterns:
|
|
- slug: two-pointers
|
|
is_optimal: true
|
|
|
|
function_signature: "def array_strings_are_equal(word1: list[str], word2: list[str]) -> bool:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { word1: ["ab", "c"], word2: ["a", "bc"] }
|
|
expected: true
|
|
- input: { word1: ["a", "cb"], word2: ["ab", "c"] }
|
|
expected: false
|
|
- input: { word1: ["abc", "d", "defg"], word2: ["abcddefg"] }
|
|
expected: true
|
|
hidden:
|
|
- input: { word1: ["a"], word2: ["a"] }
|
|
expected: true
|
|
- input: { word1: ["a", "b", "c"], word2: ["abc"] }
|
|
expected: true
|
|
- input: { word1: ["ab"], word2: ["a", "b"] }
|
|
expected: true
|
|
- input: { word1: ["abc"], word2: ["ab"] }
|
|
expected: false
|
|
|
|
description: |
|
|
Given two string arrays `word1` and `word2`, return `true` *if the two arrays **represent** the same string, and* `false` *otherwise*.
|
|
|
|
A string is **represented** by an array if the array elements concatenated **in order** forms the string.
|
|
|
|
constraints: |
|
|
- `1 <= word1.length, word2.length <= 10^3`
|
|
- `1 <= word1[i].length, word2[i].length <= 10^3`
|
|
- `1 <= sum(word1[i].length), sum(word2[i].length) <= 10^3`
|
|
- `word1[i]` and `word2[i]` consist of lowercase letters.
|
|
|
|
examples:
|
|
- input: 'word1 = ["ab", "c"], word2 = ["a", "bc"]'
|
|
output: "true"
|
|
explanation: 'word1 represents string "ab" + "c" -> "abc". word2 represents string "a" + "bc" -> "abc". The strings are the same, so return true.'
|
|
- input: 'word1 = ["a", "cb"], word2 = ["ab", "c"]'
|
|
output: "false"
|
|
explanation: 'word1 represents "acb" while word2 represents "abc". These are different strings.'
|
|
- input: 'word1 = ["abc", "d", "defg"], word2 = ["abcddefg"]'
|
|
output: "true"
|
|
explanation: 'Both arrays represent the same string "abcddefg".'
|
|
|
|
explanation:
|
|
intuition: |
|
|
Think of each string array as a sequence of puzzle pieces that, when placed side by side, form a complete word.
|
|
|
|
The question asks: do both puzzles, when assembled, spell the same word?
|
|
|
|
The simplest mental model is to **concatenate all the pieces** from each array and compare the resulting strings. If `"ab" + "c"` equals `"a" + "bc"`, both represent `"abc"` and we return `true`.
|
|
|
|
However, there's a more elegant approach: instead of building the full strings (which uses extra memory), we can compare the arrays **character by character** using pointers. Imagine two readers, each starting at the first character of the first word in their respective arrays. They move through their arrays character by character, and if at any point they see different characters, the arrays are not equivalent.
|
|
|
|
approach: |
|
|
We can solve this in two ways. The straightforward approach uses string concatenation, while the optimal approach uses two pointers for O(1) space.
|
|
|
|
**Approach 1: String Concatenation**
|
|
|
|
- Join all strings in `word1` into one string
|
|
- Join all strings in `word2` into one string
|
|
- Compare the two resulting strings
|
|
|
|
|
|
|
|
**Approach 2: Two Pointers (Space Optimal)**
|
|
|
|
**Step 1: Initialise four pointers**
|
|
|
|
- `i`: index of the current word in `word1`
|
|
- `j`: index of the current character within `word1[i]`
|
|
- `k`: index of the current word in `word2`
|
|
- `l`: index of the current character within `word2[k]`
|
|
|
|
|
|
|
|
**Step 2: Compare characters one by one**
|
|
|
|
- While both arrays have characters remaining:
|
|
- Compare `word1[i][j]` with `word2[k][l]`
|
|
- If they differ, return `false`
|
|
- Advance `j` and `l` to the next character
|
|
- If `j` reaches the end of `word1[i]`, move to the next word (`i += 1`, `j = 0`)
|
|
- If `l` reaches the end of `word2[k]`, move to the next word (`k += 1`, `l = 0`)
|
|
|
|
|
|
|
|
**Step 3: Check both arrays are exhausted**
|
|
|
|
- After the loop, return `true` only if both arrays have been fully traversed
|
|
- This handles cases where one array is a prefix of the other
|
|
|
|
common_pitfalls:
|
|
- title: Using Extra Space Unnecessarily
|
|
description: |
|
|
The concatenation approach creates two new strings, each potentially up to `10^3` characters. While this works and is easy to implement, the two-pointer approach achieves the same result with O(1) extra space.
|
|
|
|
For this problem the constraints are small enough that either approach passes, but learning the pointer technique is valuable for larger inputs.
|
|
wrong_approach: "Always concatenating strings without considering space"
|
|
correct_approach: "Use two pointers to compare character by character"
|
|
|
|
- title: Forgetting to Check Array Exhaustion
|
|
description: |
|
|
When using the two-pointer approach, it's not enough to just compare matching characters. You must verify that **both arrays are fully consumed** at the end.
|
|
|
|
For example, if `word1 = ["abc"]` and `word2 = ["ab"]`, all characters in `word2` match, but `word1` has an extra character. Simply exiting the loop when one array ends would incorrectly return `true`.
|
|
wrong_approach: "Returning true as soon as the loop ends"
|
|
correct_approach: "Verify both arrays are exhausted before returning true"
|
|
|
|
- title: Off-by-One Errors in Pointer Movement
|
|
description: |
|
|
When advancing to the next word in an array, you must reset the character index to `0` and increment the word index. It's easy to forget one of these steps, causing incorrect comparisons or index out of bounds errors.
|
|
wrong_approach: "Forgetting to reset character index when moving to next word"
|
|
correct_approach: "Reset character index to 0 when word index increments"
|
|
|
|
key_takeaways:
|
|
- "**Two-pointer technique**: When comparing sequences element by element, pointers can avoid building intermediate data structures"
|
|
- "**Space optimisation**: The difference between O(n) and O(1) space matters for large inputs or memory-constrained environments"
|
|
- "**Exhaustion check**: When comparing two sequences with pointers, always verify both are fully consumed at the end"
|
|
- "**Trade-offs**: The concatenation approach is simpler to implement and debug; the pointer approach is more efficient. Choose based on constraints."
|
|
|
|
time_complexity: "O(n) where n is the total number of characters across both arrays. Each character is visited exactly once."
|
|
space_complexity: "O(1) for the two-pointer approach (only using index variables). O(n) for the concatenation approach (creating two new strings)."
|
|
|
|
solutions:
|
|
- approach_name: Two Pointers
|
|
is_optimal: true
|
|
code: |
|
|
def array_strings_are_equal(word1: list[str], word2: list[str]) -> bool:
|
|
# Pointers for word1: i = word index, j = char index within word
|
|
i, j = 0, 0
|
|
# Pointers for word2: k = word index, l = char index within word
|
|
k, l = 0, 0
|
|
|
|
# Compare character by character
|
|
while i < len(word1) and k < len(word2):
|
|
# If current characters don't match, arrays are not equivalent
|
|
if word1[i][j] != word2[k][l]:
|
|
return False
|
|
|
|
# Move to next character in word1
|
|
j += 1
|
|
if j == len(word1[i]):
|
|
# Finished current word, move to next word
|
|
i += 1
|
|
j = 0
|
|
|
|
# Move to next character in word2
|
|
l += 1
|
|
if l == len(word2[k]):
|
|
# Finished current word, move to next word
|
|
k += 1
|
|
l = 0
|
|
|
|
# Both arrays must be fully traversed for equivalence
|
|
return i == len(word1) and k == len(word2)
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Each character in both arrays is visited exactly once.
|
|
|
|
**Space Complexity:** O(1) — Only four integer pointers are used, regardless of input size.
|
|
|
|
We traverse both arrays simultaneously, comparing characters without building intermediate strings. The pointers track our position within each array at two levels: which word we're in, and which character within that word.
|
|
|
|
- approach_name: String Concatenation
|
|
is_optimal: false
|
|
code: |
|
|
def array_strings_are_equal(word1: list[str], word2: list[str]) -> bool:
|
|
# Join all words in each array into a single string
|
|
str1 = ''.join(word1)
|
|
str2 = ''.join(word2)
|
|
|
|
# Compare the concatenated strings
|
|
return str1 == str2
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Joining and comparing strings both take linear time.
|
|
|
|
**Space Complexity:** O(n) — Two new strings are created, each up to n characters.
|
|
|
|
This approach is simpler and more readable. It works well for this problem's constraints but uses extra memory proportional to the total string length.
|