835. Image Overlap
Problem Description
You have two binary square matrices img1
and img2
, both of size n x n
, containing only 0
s and 1
s.
The task is to find the maximum overlap between these two images when you translate (slide) one image over the other. Translation means you can move one image left, right, up, or down by any number of units, then place it on top of the other image. The overlap is calculated by counting how many positions have 1
in both images at the same location after the translation.
Key points to understand:
- You can only translate (slide) the image - no rotation is allowed
- Any
1
bits that move outside the matrix boundaries during translation are discarded - The overlap count is the number of positions where both images have
1
after one is translated - You need to find the maximum possible overlap value
For example, if you have two 3x3 matrices and you slide one of them, you count how many 1
s align with 1
s in the other matrix. You try different translations and return the highest overlap count you can achieve.
The solution works by considering every possible translation offset (dx, dy)
between pairs of 1
s in both images. For each 1
at position (i, j)
in img1
and each 1
at position (h, k)
in img2
, the offset (i - h, j - k)
represents how much we need to translate img2
to align these two 1
s. By counting occurrences of each offset using a hash table, the most frequent offset gives us the maximum overlap.
Intuition
Think about what happens when we translate one image to achieve an overlap. When two 1
s from different images align, they must have been moved by the same translation offset.
If a 1
at position (i, j)
in img1
aligns with a 1
at position (h, k)
in img2
, it means we've translated img2
by offset (i - h, j - k)
. The key insight is that all pairs of 1
s that align under the same translation will have the exact same offset.
For example, if translating img2
by offset (2, -1)
causes three different pairs of 1
s to align, then all three pairs will have the offset (2, -1)
when we calculate (i - h, j - k)
for each pair.
This observation leads us to the solution strategy: instead of trying every possible translation (which would be expensive), we can:
- Look at every possible pair of
1
s between the two images - Calculate what translation offset would align each pair
- Count how many pairs share the same offset
The offset that appears most frequently tells us the translation that creates the maximum number of aligned 1
s - which is exactly the maximum overlap we're looking for.
By using a hash table to count occurrences of each offset (dx, dy)
, we efficiently find which translation produces the most overlapping 1
s. The maximum count in this hash table is our answer.
Solution Approach
The implementation uses a hash table to count the frequency of each translation offset:
-
Initialize a Counter: We use a
Counter
(hash table) calledcnt
to track how many times each offset appears. -
Iterate through all
1
s in img1: We use nested loops to find each position(i, j)
whereimg1[i][j] == 1
. -
For each
1
in img1, find all1
s in img2: For every1
found inimg1
, we scan through all positions(h, k)
inimg2
to find whereimg2[h][k] == 1
. -
Calculate the offset: For each pair of
1
s (one fromimg1
at(i, j)
and one fromimg2
at(h, k)
), we calculate the translation offset as(i - h, j - k)
. This represents how much we need to shiftimg2
to align these two specific1
s. -
Count the offset frequency: We increment the count for this offset in our hash table:
cnt[(i - h, j - k)] += 1
. Each increment means we found another pair of1
s that would align under this same translation. -
Find the maximum overlap: After processing all pairs, the maximum value in
cnt
represents the largest number of1
s that can be aligned with a single translation. We return this value usingmax(cnt.values())
. -
Handle edge case: If there are no
1
s in either image (meaningcnt
is empty), we return0
.
The time complexity is O(n^4)
since we have four nested loops iterating through the matrices. The space complexity is O(n^2)
for storing the offsets in the hash table, as there can be at most n^2
different offsets.
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 walk through a small example with two 3x3 matrices:
img1 = [[1,1,0], img2 = [[0,0,0], [0,1,0], [0,1,1], [0,0,0]] [0,0,1]]
Step 1: Find all 1s in img1
- Position (0,0): value is 1
- Position (0,1): value is 1
- Position (1,1): value is 1
Step 2: For each 1 in img1, pair with all 1s in img2
For img1[0][0] = 1:
- Pairs with img2[1][1] = 1: offset = (0-1, 0-1) = (-1, -1)
- Pairs with img2[1][2] = 1: offset = (0-1, 0-2) = (-1, -2)
- Pairs with img2[2][2] = 1: offset = (0-2, 0-2) = (-2, -2)
For img1[0][1] = 1:
- Pairs with img2[1][1] = 1: offset = (0-1, 1-1) = (-1, 0)
- Pairs with img2[1][2] = 1: offset = (0-1, 1-2) = (-1, -1)
- Pairs with img2[2][2] = 1: offset = (0-2, 1-2) = (-2, -1)
For img1[1][1] = 1:
- Pairs with img2[1][1] = 1: offset = (1-1, 1-1) = (0, 0)
- Pairs with img2[1][2] = 1: offset = (1-1, 1-2) = (0, -1)
- Pairs with img2[2][2] = 1: offset = (1-2, 1-2) = (-1, -1)
Step 3: Count offset frequencies
Offset (-1, -1): appears 3 times Offset (-1, -2): appears 1 time Offset (-2, -2): appears 1 time Offset (-1, 0): appears 1 time Offset (-2, -1): appears 1 time Offset (0, 0): appears 1 time Offset (0, -1): appears 1 time
Step 4: Find maximum The offset (-1, -1) appears 3 times, which is the maximum.
Verification: If we translate img2 by offset (-1, -1) (shift up by 1 and left by 1):
Original img2: After translation: [[0,0,0], [[1,1,0], [0,1,1], → [0,1,*], [0,0,1]] [*,*,*]] (* means out of bounds)
Overlaying this on img1:
img1: Translated img2: Overlap: [[1,1,0], [[1,1,0], 3 positions [0,1,0], + [0,1,*], = with 1s in both [0,0,0]] [*,*,*]] (0,0), (0,1), (1,1)
The maximum overlap is 3.
Solution Implementation
1class Solution:
2 def largestOverlap(self, img1: List[List[int]], img2: List[List[int]]) -> int:
3 """
4 Find the largest overlap between two binary images by sliding one over the other.
5
6 The algorithm works by:
7 1. Finding all '1' positions in both images
8 2. For each pair of '1's (one from each image), calculating the translation vector
9 3. Counting how many '1's align for each unique translation
10 4. Returning the maximum count
11
12 Args:
13 img1: First n x n binary matrix
14 img2: Second n x n binary matrix
15
16 Returns:
17 Maximum number of overlapping 1's after translation
18 """
19 n = len(img1)
20
21 # Dictionary to count overlaps for each translation vector (row_shift, col_shift)
22 translation_count = Counter()
23
24 # Iterate through all positions in img1
25 for row1 in range(n):
26 for col1 in range(n):
27 # If current position in img1 contains a 1
28 if img1[row1][col1] == 1:
29 # Check all positions in img2
30 for row2 in range(n):
31 for col2 in range(n):
32 # If current position in img2 contains a 1
33 if img2[row2][col2] == 1:
34 # Calculate the translation vector needed to align
35 # these two 1's (from img1 position to img2 position)
36 translation_vector = (row1 - row2, col1 - col2)
37
38 # Increment count for this translation
39 translation_count[translation_vector] += 1
40
41 # Return the maximum overlap count, or 0 if no overlaps exist
42 return max(translation_count.values()) if translation_count else 0
43
1class Solution {
2 public int largestOverlap(int[][] img1, int[][] img2) {
3 int n = img1.length;
4 // Map to store translation vectors and their occurrence counts
5 // Key: translation vector [deltaRow, deltaCol], Value: count of overlapping 1s
6 Map<List<Integer>, Integer> translationCount = new HashMap<>();
7 int maxOverlap = 0;
8
9 // Iterate through all positions with 1s in img1
10 for (int row1 = 0; row1 < n; row1++) {
11 for (int col1 = 0; col1 < n; col1++) {
12 if (img1[row1][col1] == 1) {
13 // For each 1 in img1, check all 1s in img2
14 for (int row2 = 0; row2 < n; row2++) {
15 for (int col2 = 0; col2 < n; col2++) {
16 if (img2[row2][col2] == 1) {
17 // Calculate the translation vector needed to align
18 // current 1 in img1 with current 1 in img2
19 List<Integer> translationVector = List.of(row1 - row2, col1 - col2);
20
21 // Increment count for this translation vector and update max
22 // merge() adds 1 to existing count or initializes to 1 if new
23 int currentCount = translationCount.merge(translationVector, 1, Integer::sum);
24 maxOverlap = Math.max(maxOverlap, currentCount);
25 }
26 }
27 }
28 }
29 }
30 }
31
32 return maxOverlap;
33 }
34}
35
1class Solution {
2public:
3 int largestOverlap(vector<vector<int>>& img1, vector<vector<int>>& img2) {
4 int n = img1.size();
5 // Map to store the frequency of each translation vector (deltaRow, deltaCol)
6 // Key: translation vector, Value: count of overlapping 1s for this translation
7 map<pair<int, int>, int> translationCount;
8 int maxOverlap = 0;
9
10 // Iterate through all cells with value 1 in img1
11 for (int row1 = 0; row1 < n; ++row1) {
12 for (int col1 = 0; col1 < n; ++col1) {
13 if (img1[row1][col1] == 1) {
14 // For each 1 in img1, check all 1s in img2
15 for (int row2 = 0; row2 < n; ++row2) {
16 for (int col2 = 0; col2 < n; ++col2) {
17 if (img2[row2][col2] == 1) {
18 // Calculate the translation vector needed to align
19 // the current 1 in img1 with the current 1 in img2
20 int deltaRow = row1 - row2;
21 int deltaCol = col1 - col2;
22
23 // Increment count for this translation vector and update max
24 translationCount[{deltaRow, deltaCol}]++;
25 maxOverlap = max(maxOverlap, translationCount[{deltaRow, deltaCol}]);
26 }
27 }
28 }
29 }
30 }
31 }
32
33 return maxOverlap;
34 }
35};
36
1/**
2 * Finds the largest overlap between two binary images after translation
3 * @param img1 - First binary image represented as 2D array
4 * @param img2 - Second binary image represented as 2D array
5 * @returns Maximum number of overlapping 1s after optimal translation
6 */
7function largestOverlap(img1: number[][], img2: number[][]): number {
8 const imageSize: number = img1.length;
9
10 // Map to store translation vectors and their corresponding overlap counts
11 // Key: encoded translation vector (row_offset * 200 + col_offset)
12 // Value: count of overlapping 1s for this translation
13 const translationCountMap: Map<number, number> = new Map();
14
15 let maxOverlap: number = 0;
16
17 // Iterate through all positions with value 1 in img1
18 for (let row1 = 0; row1 < imageSize; ++row1) {
19 for (let col1 = 0; col1 < imageSize; ++col1) {
20 if (img1[row1][col1] === 1) {
21
22 // For each 1 in img1, check all possible overlaps with 1s in img2
23 for (let row2 = 0; row2 < imageSize; ++row2) {
24 for (let col2 = 0; col2 < imageSize; ++col2) {
25 if (img2[row2][col2] === 1) {
26
27 // Calculate translation vector from img1 position to img2 position
28 // Encode as single number: row_offset * 200 + col_offset
29 // 200 is chosen as it's larger than max possible offset (n < 100)
30 const translationKey: number = (row1 - row2) * 200 + (col1 - col2);
31
32 // Increment count for this translation vector
33 const currentCount: number = (translationCountMap.get(translationKey) ?? 0) + 1;
34 translationCountMap.set(translationKey, currentCount);
35
36 // Update maximum overlap found so far
37 maxOverlap = Math.max(maxOverlap, currentCount);
38 }
39 }
40 }
41 }
42 }
43 }
44
45 return maxOverlap;
46}
47
Time and Space Complexity
Time Complexity: O(n^4)
The code contains four nested loops:
- The outer two loops iterate through all positions in
img1
, each runningn
times - For each position where
img1[i][j] = 1
, the inner two loops iterate through all positions inimg2
, each runningn
times - In the worst case where all elements in
img1
are 1, we performn × n × n × n = n^4
operations - The dictionary update operation
cnt[(i - h, j - k)] += 1
takesO(1)
time - Finding the maximum value in the counter takes
O(n^2)
time in the worst case (as there can be at most(2n-1) × (2n-1)
different shifts), but this is dominated by theO(n^4)
loops
Space Complexity: O(n^2)
- The
cnt
Counter dictionary stores shift vectors(i - h, j - k)
as keys - The possible range of shifts is from
-(n-1)
to(n-1)
in both dimensions - This gives us at most
(2n-1) × (2n-1) = O(n^2)
possible unique shift vectors - Each entry in the counter stores a shift vector (tuple of two integers) and a count value
- Therefore, the space complexity is
O(n^2)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Confusion About Which Image to Translate
Many people get confused about whether they're translating img1
or img2
. The code calculates (row1 - row2, col1 - col2)
, which represents translating img2
to align with img1
. A common mistake is reversing this logic or inconsistently applying translations.
Solution: Be consistent with your translation direction. The current approach treats img1
as fixed and translates img2
. The offset (row1 - row2, col1 - col2)
means "how much to shift img2 to align its 1 at (row2, col2) with img1's 1 at (row1, col1)."
2. Attempting to Optimize Prematurely by Only Storing 1's Positions
Some might try to optimize by first extracting all positions of 1's into separate lists, then iterating through these lists. While this seems efficient, it can lead to more complex code and potential indexing errors.
# Problematic approach
ones_img1 = [(i, j) for i in range(n) for j in range(n) if img1[i][j] == 1]
ones_img2 = [(i, j) for i in range(n) for j in range(n) if img2[i][j] == 1]
# This adds complexity without significant performance gain for typical inputs
Solution: The straightforward nested loop approach is cleaner and easier to understand. Only optimize if performance becomes an actual issue with large inputs.
3. Forgetting to Handle Empty Counter
If either image contains no 1's, the translation_count
dictionary will be empty, and calling max(translation_count.values())
will throw a ValueError
.
Solution: Always check if the counter is non-empty before calling max()
:
return max(translation_count.values()) if translation_count else 0
4. Misunderstanding Translation vs. Rotation
Some might think they need to consider rotations or flips of the images. The problem only allows translations (sliding), not rotations.
Solution: Focus only on horizontal and vertical shifts. Each translation is uniquely defined by a (row_shift, col_shift)
pair.
5. Incorrect Offset Calculation
A common error is calculating the offset as (row2 - row1, col2 - col1)
instead of (row1 - row2, col1 - col2)
. This reversal would give incorrect results as it represents the opposite translation direction.
Solution: Remember that the offset represents how to move img2
to align with img1
. If a 1 in img1
is at position (3, 4) and a 1 in img2
is at position (1, 2), we need to shift img2
by (3-1, 4-2) = (2, 2) to align these 1's.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!