Hello, reader ๐๐ฝ ! Welcome to day 27 of the series on Problem Solving. Through this series, I aim to pick up at least one question everyday and share my approach for solving it.
Today, I will be picking up LeetCode's daily challenge problem: 835. Image Overlap.
๐ค Problem Statement
- There are two images, img1 and img2, represented as binary, square matrices of size n x n. A binary matrix has only 0s and 1s as values.
- One of the images is translated by sliding all the 1 bits left, right, up, and/or down any number of units.
- Then one of the images is placed on top of the other image. The overlap is calculated by counting the number of positions that have a 1 in both images.
- Return the largest possible overlap.
- E.g.:
img1 = [[1,1,0],[0,1,0],[0,1,0]], img2 = [[0,0,0],[0,1,1],[0,0,1]]
=>3
img1 = [[0,0,0,0,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]], img2 = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[1,0,0,0,0]]
=>1
๐ฌ Thought Process - Brute Force
- Not going to lie, this problem was not at all intuitive. You need a sense to visualise the matrix over a graph/ plan to understand and come up with a solution.
- The first way to think about this problem is generating all the possible translations.
- And after each translation we will try to look into the overlapping 1's.
- Let's split this problem into 2 steps:
- Coming up with an algorithm to translate the image.
- Counting the overlaps.
Step 1: Translate the image
- We can translate an image by moving around 1 image and keeping the other static.
- That is, we move 1st matrix up and keep the other matrix static.
- We also have to translate down. This can be done by keeping the 1st matrix static and moving the 2nd matrix up => This is same as moving image down
- So basically, we translate the matrices in the following way:
- Move matrix 1 left up, keep matrix 2 static
- Move matrix 2 left and up, keep matrix 1 static
Solution Step 1: Translate the image
public void translateImage(int[][] img1, int[][] img2) {
int overlaps = 0;
int n = img1.length;
for(int i = 0; i<n; i++) {
for(int j = 0; j<n; j++) {
// move img1 right and down
overlaps = Math.max(overlaps, countOverlap(img1, img2, i, j));
// move img2 right and down
// same as moving img1 left and up
overlaps = Math.max(overlaps, countOverlap(img2, img1, i, j));
}
}
return overlaps;
}
Step 2: Count Overlaps
- In the previous step, we moved the matrix either to the left and up or left and down.
- In this step, we will calculate the overlaps after the translation.
- But there is another thing we need to keep in mind. Given a shift in y axis (up or down), the x axis can be shifted either to the right or left.
- Our code deals with only shifting to the left for the y direction. We also need to deal with moving right.
- Here's what I mean Let's assume we have a
5x5
matrix with the following values: Let's focus on shifting the row and column by 1 row and 1 column.
- So first we shift the matrix A to up and left. While calculating the overlap, we also check for the scenario to shift right and up.
- Similarly, when we shift matrix B to up and left, we check for the scenario to shift right and up while calculating overlap.
Below is the image showing the 4 types of translation:
Solution Step 2: Count Overlaps
private int countOverlap(int[][] a, int[][] b,
int rowStart, int colStart) {
int n = a.length;
int sumLeft = 0, sumRight = 0;
int bRow = 0, bCol = 0;
for(int aRow = rowStart; aRow<n; aRow++) {
bCol = 0;
for(int aCol = colStart; aCol<n; aCol++) {
// Calculate when a is shifted to left
if(a[aRow][aCol] == 1 && a[aRow][aCol] == b[bRow][bCol]) {
sumLeft++;
}
// calculate when a is shifted to right
// (or b shifted to left)
if(a[aRow][bCol] == 1 && b[bRow][aCol] == a[aRow][bCol]) {
sumRight++;
}
bCol++;
}
bRow++;
}
return Math.max(sumLeft, sumRight);
}
๐ฉ๐ฝโ๐ป Solution - Brute Force
class Solution {
public int largestOverlap(int[][] img1, int[][] img2) {
int overlaps = 0;
int n = img1.length;
for(int i = 0; i<n; i++) {
for(int j = 0; j<n; j++) {
overlaps = Math.max(overlaps, countOverlap(img1, img2, i, j));
overlaps = Math.max(overlaps, countOverlap(img2, img1, i, j));
}
}
return overlaps;
}
private int countOverlap(int[][] a, int[][] b, int rowStart, int colStart) {
int n = a.length;
int sumLeft = 0, sumRight = 0;
int bRow = 0, bCol = 0;
for(int aRow = rowStart; aRow<n; aRow++) {
bCol = 0;
for(int aCol = colStart; aCol<n; aCol++) {
// Calculate when a is shifted to left
if(a[aRow][aCol] == 1 && a[aRow][aCol] == b[bRow][bCol]) {
sumLeft++;
}
// calculate when a is shifted to right
// (or b shifted to left)
if(a[aRow][bCol] == 1 && b[bRow][aCol] == a[aRow][bCol]) {
sumRight++;
}
bCol++;
}
bRow++;
}
return Math.max(sumLeft, sumRight);
}
}
Time Complexity: O(n^4)
- n = number of rows/ cols
- For every translation of <i,j> we are checking for overlaps on both the matrices.
Space Complexity: O(1)
- From LeetCode, you can find two other solutions as well. But in terms of space and run-time this is the best we can do (and most intuitive out of the 3).
The only draw back is that we might do unnecessary transformations for cases when there are very few 1s and lots of 0s.
You can find the code for this question in the GitHub repo here: 835. Image Overlap.
Conclusion
That's a wrap for today's problem. If you liked my explanation then please do drop a like/ comment. Also, please correct me if I've made any mistakes or if you want me to improve something!
Thank you for reading!