questions A (01-matrix - avoid-flood)
This commit is contained in:
166
backend/data/questions/4sum-ii.yaml
Normal file
166
backend/data/questions/4sum-ii.yaml
Normal file
@@ -0,0 +1,166 @@
|
||||
title: 4Sum II
|
||||
slug: 4sum-ii
|
||||
difficulty: medium
|
||||
leetcode_id: 454
|
||||
leetcode_url: https://leetcode.com/problems/4sum-ii/
|
||||
categories:
|
||||
- arrays
|
||||
- hash-tables
|
||||
patterns:
|
||||
- two-pointers
|
||||
|
||||
description: |
|
||||
Given four integer arrays `nums1`, `nums2`, `nums3`, and `nums4` all of length `n`, return the number of tuples `(i, j, k, l)` such that:
|
||||
|
||||
- `0 <= i, j, k, l < n`
|
||||
- `nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0`
|
||||
|
||||
constraints: |
|
||||
- `n == nums1.length == nums2.length == nums3.length == nums4.length`
|
||||
- `1 <= n <= 200`
|
||||
- `-2^28 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 2^28`
|
||||
|
||||
examples:
|
||||
- input: "nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]"
|
||||
output: "2"
|
||||
explanation: "The two tuples are: (0, 0, 0, 1) -> 1 + (-2) + (-1) + 2 = 0, and (1, 1, 0, 0) -> 2 + (-1) + (-1) + 0 = 0."
|
||||
- input: "nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]"
|
||||
output: "1"
|
||||
explanation: "The only tuple is (0, 0, 0, 0) -> 0 + 0 + 0 + 0 = 0."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
At first glance, this looks like a problem requiring four nested loops to check every combination — but that would be O(n^4), far too slow.
|
||||
|
||||
The key insight is to **split the problem in half**. Think of it like this: instead of finding four numbers that sum to zero, find two numbers from the first half (arrays 1 and 2) and two numbers from the second half (arrays 3 and 4) that cancel each other out.
|
||||
|
||||
If `a + b + c + d = 0`, then `a + b = -(c + d)`.
|
||||
|
||||
This transforms the problem into a **Two Sum variant**: for every possible sum from the first two arrays, check how many times its negation appears among sums from the last two arrays.
|
||||
|
||||
By precomputing all possible sums from arrays 1 and 2 into a hash map, we can then iterate through arrays 3 and 4 and look up complements in O(1) time. This reduces the complexity from O(n^4) to O(n^2).
|
||||
|
||||
approach: |
|
||||
We solve this using a **Hash Map with Split Arrays** approach:
|
||||
|
||||
**Step 1: Build a hash map of sums from the first two arrays**
|
||||
|
||||
- Create an empty hash map `sum_count` to store `{sum: frequency}`
|
||||
- Iterate through all pairs `(a, b)` from `nums1` and `nums2`
|
||||
- For each pair, calculate `a + b` and increment its count in the hash map
|
||||
|
||||
|
||||
|
||||
**Step 2: Count complements from the last two arrays**
|
||||
|
||||
- Initialise `count = 0` to track the total number of valid tuples
|
||||
- Iterate through all pairs `(c, d)` from `nums3` and `nums4`
|
||||
- For each pair, calculate `target = -(c + d)`
|
||||
- If `target` exists in `sum_count`, add `sum_count[target]` to our count
|
||||
|
||||
|
||||
|
||||
**Step 3: Return the result**
|
||||
|
||||
- Return `count` as the total number of tuples that sum to zero
|
||||
|
||||
|
||||
|
||||
This approach works because each time we find a matching complement, we're counting all valid combinations: if `a + b` appears 3 times and `-(c + d)` matches it, that's 3 valid tuples for this specific `(c, d)` pair.
|
||||
|
||||
common_pitfalls:
|
||||
- title: The Brute Force Trap
|
||||
description: |
|
||||
The naive approach uses four nested loops to check every possible `(i, j, k, l)` combination:
|
||||
|
||||
```python
|
||||
for i in nums1:
|
||||
for j in nums2:
|
||||
for k in nums3:
|
||||
for l in nums4:
|
||||
if i + j + k + l == 0:
|
||||
count += 1
|
||||
```
|
||||
|
||||
This results in **O(n^4) time complexity**. With `n = 200`, that's 1.6 billion operations — guaranteed TLE.
|
||||
wrong_approach: "Four nested loops checking all combinations"
|
||||
correct_approach: "Split into two groups and use hash map for O(n^2)"
|
||||
|
||||
- title: Forgetting to Count Duplicates
|
||||
description: |
|
||||
A common mistake is using a set instead of a counting map for the first two arrays. If the same sum `a + b` can be formed in multiple ways (e.g., `1 + 2` and `0 + 3` both equal 3), each occurrence represents a different valid tuple.
|
||||
|
||||
Using a set would only count one match per sum value, missing valid combinations.
|
||||
wrong_approach: "Using a set to store sums from first two arrays"
|
||||
correct_approach: "Using a hash map with frequency counts"
|
||||
|
||||
- title: Looking Up Wrong Complement
|
||||
description: |
|
||||
When searching for complements, ensure you're looking for `-(c + d)`, not `(c + d)`. We need the sum from the first half to be the **negation** of the sum from the second half so they cancel to zero.
|
||||
wrong_approach: "Looking up (c + d) in the hash map"
|
||||
correct_approach: "Looking up -(c + d) to find values that sum to zero"
|
||||
|
||||
key_takeaways:
|
||||
- "**Divide and conquer with hashing**: When dealing with multiple arrays, consider splitting them and using a hash map to bridge the halves"
|
||||
- "**Two Sum generalisation**: This is essentially Two Sum where each 'number' is a sum of elements from two arrays"
|
||||
- "**Time-space tradeoff**: We use O(n^2) extra space to reduce time from O(n^4) to O(n^2)"
|
||||
- "**Count, don't just detect**: When duplicates matter, use a frequency map instead of a set"
|
||||
|
||||
time_complexity: "O(n^2). We iterate through n^2 pairs for the first two arrays to build the map, then another n^2 pairs for the last two arrays to find complements."
|
||||
space_complexity: "O(n^2). The hash map can store up to n^2 different sums from the first two arrays."
|
||||
|
||||
solutions:
|
||||
- approach_name: Hash Map with Split Arrays
|
||||
is_optimal: true
|
||||
code: |
|
||||
from collections import defaultdict
|
||||
|
||||
def four_sum_count(nums1: list[int], nums2: list[int],
|
||||
nums3: list[int], nums4: list[int]) -> int:
|
||||
# Store all possible sums from first two arrays with their frequencies
|
||||
sum_count = defaultdict(int)
|
||||
|
||||
# Build the hash map: O(n^2)
|
||||
for a in nums1:
|
||||
for b in nums2:
|
||||
sum_count[a + b] += 1
|
||||
|
||||
# Count complements from last two arrays: O(n^2)
|
||||
count = 0
|
||||
for c in nums3:
|
||||
for d in nums4:
|
||||
# We need a + b + c + d = 0, so a + b = -(c + d)
|
||||
target = -(c + d)
|
||||
# Add the number of ways to form this sum from first two arrays
|
||||
count += sum_count[target]
|
||||
|
||||
return count
|
||||
explanation: |
|
||||
**Time Complexity:** O(n^2) — Two passes of n^2 iterations each.
|
||||
|
||||
**Space Complexity:** O(n^2) — Hash map stores up to n^2 sums.
|
||||
|
||||
We split the four arrays into two groups. First, we precompute all sums from arrays 1 and 2, storing their frequencies. Then, for each sum from arrays 3 and 4, we look up how many complementary sums exist. The `defaultdict(int)` returns 0 for missing keys, simplifying the lookup.
|
||||
|
||||
- approach_name: Brute Force
|
||||
is_optimal: false
|
||||
code: |
|
||||
def four_sum_count(nums1: list[int], nums2: list[int],
|
||||
nums3: list[int], nums4: list[int]) -> int:
|
||||
count = 0
|
||||
|
||||
# Check every possible combination of indices
|
||||
for a in nums1:
|
||||
for b in nums2:
|
||||
for c in nums3:
|
||||
for d in nums4:
|
||||
if a + b + c + d == 0:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
explanation: |
|
||||
**Time Complexity:** O(n^4) — Four nested loops.
|
||||
|
||||
**Space Complexity:** O(1) — Only a counter variable.
|
||||
|
||||
This approach checks every possible tuple directly. While correct, it's prohibitively slow for the given constraints (n up to 200). Included to illustrate why the hash map optimisation is essential.
|
||||
Reference in New Issue
Block a user