533. Lonely Pixel II 🔒
Problem Description
You are given an m x n
grid called picture
where each cell contains either a black pixel represented by 'B'
or a white pixel represented by 'W'
. You also have an integer target
.
Your task is to count the number of black lonely pixels in the picture.
A black pixel at position (r, c)
is considered "lonely" if it meets two specific conditions:
-
Count condition: Both row
r
and columnc
must contain exactlytarget
black pixels (including the pixel at(r, c)
itself). -
Row matching condition: All rows that contain a black pixel in column
c
must be identical to rowr
. In other words, if columnc
has black pixels in multiple rows, those rows must have the exact same pattern of black and white pixels.
For example, if target = 3
and a black pixel at position (2, 4)
is to be lonely:
- Row 2 must have exactly 3 black pixels total
- Column 4 must have exactly 3 black pixels total
- If column 4 has black pixels in rows 2, 5, and 7, then rows 2, 5, and 7 must be identical (same pattern of
'B'
and'W'
in all positions)
The goal is to return the total count of all black lonely pixels in the entire picture.
Intuition
The key insight is recognizing that the second condition creates a strong constraint: if multiple rows have black pixels in the same column, those rows must be identical. This means we can group rows by columns and check if they're all the same.
Let's think about what we need to track:
- For each row, we need to know how many black pixels it contains (to check if it equals
target
) - For each column, we need to know which rows have black pixels in that column
- We need to verify that all rows with black pixels in the same column are identical
The crucial observation is that if a column j
has exactly target
black pixels, and all these black pixels are in rows that are identical to each other, then all these black pixels are lonely pixels. Why? Because:
- Each of these identical rows has exactly
target
black pixels (since they're the same) - Column
j
has exactlytarget
black pixels (we know this) - The row matching condition is satisfied (all rows are identical)
This leads us to a counting strategy:
- First, count black pixels in each row
- Group rows by the columns where they have black pixels
- For each column, check if the first row in that column's group has exactly
target
black pixels - If yes, verify all rows in that group are identical
- If they are, then all
target
black pixels in that column are lonely, so we addtarget
to our answer
The efficiency comes from recognizing that we don't need to check each black pixel individually. Instead, we can process entire columns at once, adding target
lonely pixels whenever we find a valid column.
Solution Approach
We implement the solution using a counting and grouping strategy with the following data structures:
-
Row counting array
rows
: An array whererows[i]
stores the count of black pixels in rowi
. -
Column-to-rows mapping
g
: A dictionary whereg[j]
contains a list of all row indices that have a black pixel in columnj
.
Step 1: Build the data structures
We iterate through the entire picture once:
for i, row in enumerate(picture):
for j, x in enumerate(row):
if x == "B":
rows[i] += 1 # Count black pixels in row i
g[j].append(i) # Record that row i has a black pixel in column j
Step 2: Process each column
For each column j
that contains black pixels (exists in g
):
-
Get the first row
i1
that has a black pixel in columnj
:i1 = g[j][0]
-
Check if row
i1
has exactlytarget
black pixels:- If
rows[i1] != target
, skip this column as it can't contain lonely pixels
- If
-
Verify the two conditions for lonely pixels:
- Check if the number of rows with black pixels in column
j
equalstarget
:len(g[j]) == rows[i1]
- Since
rows[i1] = target
, this ensures columnj
has exactlytarget
black pixels
- Since
- Check if all rows with black pixels in column
j
are identical to rowi1
:all(picture[i2] == picture[i1] for i2 in g[j])
- Check if the number of rows with black pixels in column
-
If both conditions are met, all black pixels in column
j
are lonely pixels, so we addtarget
to the answer.
Why this works:
When we find a valid column j
where:
- All rows containing black pixels in that column are identical
- Each of these rows has exactly
target
black pixels - Column
j
has exactlytarget
black pixels
Then every black pixel in column j
satisfies both lonely pixel conditions, allowing us to count all target
of them at once instead of checking each pixel individually.
The time complexity is O(m × n × m)
where the extra m
factor comes from comparing rows for equality. The space complexity is O(m × n)
for storing the row patterns in the worst case.
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 concrete example with a 3×4 grid and target = 2
:
picture = [ ['W', 'B', 'W', 'B'], # Row 0 ['W', 'B', 'W', 'B'], # Row 1 ['B', 'W', 'B', 'W'] # Row 2 ] target = 2
Step 1: Build data structures
First, we count black pixels in each row and track which rows have black pixels in each column:
- Row 0: Contains 2 black pixels (at positions [0,1] and [0,3])
- Row 1: Contains 2 black pixels (at positions [1,1] and [1,3])
- Row 2: Contains 2 black pixels (at positions [2,0] and [2,2])
Our data structures become:
rows = [2, 2, 2]
(each row has 2 black pixels)g = {0: [2], 1: [0, 1], 2: [2], 3: [0, 1]}
- Column 0: black pixel in row 2
- Column 1: black pixels in rows 0 and 1
- Column 2: black pixel in row 2
- Column 3: black pixels in rows 0 and 1
Step 2: Process each column
Column 0: g[0] = [2]
- First row with black pixel:
i1 = 2
- Check if
rows[2] == target
: Yes, 2 == 2 ✓ - Check if
len(g[0]) == rows[2]
: Is 1 == 2? No ✗ - Column 0 doesn't contain lonely pixels (only 1 black pixel, but needs 2)
Column 1: g[1] = [0, 1]
- First row with black pixel:
i1 = 0
- Check if
rows[0] == target
: Yes, 2 == 2 ✓ - Check if
len(g[1]) == rows[0]
: Is 2 == 2? Yes ✓ - Check if all rows are identical: Are rows 0 and 1 the same?
- Row 0: ['W', 'B', 'W', 'B']
- Row 1: ['W', 'B', 'W', 'B']
- Yes, they're identical! ✓
- Column 1 contains 2 lonely pixels. Add 2 to answer.
Column 2: g[2] = [2]
- First row with black pixel:
i1 = 2
- Check if
rows[2] == target
: Yes, 2 == 2 ✓ - Check if
len(g[2]) == rows[2]
: Is 1 == 2? No ✗ - Column 2 doesn't contain lonely pixels
Column 3: g[3] = [0, 1]
- First row with black pixel:
i1 = 0
- Check if
rows[0] == target
: Yes, 2 == 2 ✓ - Check if
len(g[3]) == rows[0]
: Is 2 == 2? Yes ✓ - Check if all rows are identical: Yes (rows 0 and 1 are identical) ✓
- Column 3 contains 2 lonely pixels. Add 2 to answer.
Final Answer: 4
The lonely pixels are at positions (0,1), (1,1), (0,3), and (1,3). Each satisfies both conditions:
- Their rows have exactly 2 black pixels
- Their columns have exactly 2 black pixels
- All rows with black pixels in their columns are identical
Solution Implementation
1from typing import List
2from collections import defaultdict
3
4class Solution:
5 def findBlackPixel(self, picture: List[List[str]], target: int) -> int:
6 # Count black pixels in each row
7 black_pixels_per_row = [0] * len(picture)
8
9 # Map each column to list of row indices that have black pixels in that column
10 column_to_row_indices = defaultdict(list)
11
12 # First pass: count black pixels per row and track which rows have black pixels in each column
13 for row_idx, row in enumerate(picture):
14 for col_idx, pixel in enumerate(row):
15 if pixel == "B":
16 black_pixels_per_row[row_idx] += 1
17 column_to_row_indices[col_idx].append(row_idx)
18
19 # Count valid black pixels
20 total_valid_pixels = 0
21
22 # Check each column that has black pixels
23 for col_idx in column_to_row_indices:
24 # Get the first row index that has a black pixel in this column
25 first_row_idx = column_to_row_indices[col_idx][0]
26
27 # Skip if the first row doesn't have exactly 'target' black pixels
28 if black_pixels_per_row[first_row_idx] != target:
29 continue
30
31 # Check if this column meets the criteria:
32 # 1. Number of black pixels in column equals number of black pixels in the first row
33 # 2. All rows with black pixels in this column are identical to the first row
34 num_black_in_column = len(column_to_row_indices[col_idx])
35 if (num_black_in_column == black_pixels_per_row[first_row_idx] and
36 all(picture[row_idx] == picture[first_row_idx]
37 for row_idx in column_to_row_indices[col_idx])):
38 # Add all black pixels in this column to the count
39 total_valid_pixels += target
40
41 return total_valid_pixels
42
1class Solution {
2 public int findBlackPixel(char[][] picture, int target) {
3 int numRows = picture.length;
4 int numCols = picture[0].length;
5
6 // Store list of row indices for each column that contains black pixels
7 List<Integer>[] blackPixelRowsByCol = new List[numCols];
8 Arrays.setAll(blackPixelRowsByCol, k -> new ArrayList<>());
9
10 // Count black pixels in each row
11 int[] blackPixelsPerRow = new int[numRows];
12
13 // Scan the picture to populate data structures
14 for (int row = 0; row < numRows; ++row) {
15 for (int col = 0; col < numCols; ++col) {
16 if (picture[row][col] == 'B') {
17 blackPixelsPerRow[row]++;
18 blackPixelRowsByCol[col].add(row);
19 }
20 }
21 }
22
23 int totalLonelyBlackPixels = 0;
24
25 // Check each column for valid black pixels
26 for (int col = 0; col < numCols; ++col) {
27 // Skip if column has no black pixels or first row doesn't have exactly target black pixels
28 if (blackPixelRowsByCol[col].isEmpty() ||
29 blackPixelsPerRow[blackPixelRowsByCol[col].get(0)] != target) {
30 continue;
31 }
32
33 int firstRowIndex = blackPixelRowsByCol[col].get(0);
34 int validPixelsInColumn = 0;
35
36 // Check if number of black pixels in column equals number in the first row
37 if (blackPixelRowsByCol[col].size() == blackPixelsPerRow[firstRowIndex]) {
38 validPixelsInColumn = target;
39
40 // Verify all rows with black pixels in this column are identical
41 for (int rowIndex : blackPixelRowsByCol[col]) {
42 if (!Arrays.equals(picture[firstRowIndex], picture[rowIndex])) {
43 validPixelsInColumn = 0;
44 break;
45 }
46 }
47 }
48
49 totalLonelyBlackPixels += validPixelsInColumn;
50 }
51
52 return totalLonelyBlackPixels;
53 }
54}
55
1class Solution {
2public:
3 int findBlackPixel(vector<vector<char>>& picture, int target) {
4 int numRows = picture.size();
5 int numCols = picture[0].size();
6
7 // Store row indices containing black pixels for each column
8 vector<vector<int>> blackPixelRowsByCol(numCols);
9
10 // Count of black pixels in each row
11 vector<int> blackPixelsPerRow(numRows);
12
13 // Populate the data structures
14 for (int row = 0; row < numRows; ++row) {
15 for (int col = 0; col < numCols; ++col) {
16 if (picture[row][col] == 'B') {
17 ++blackPixelsPerRow[row];
18 blackPixelRowsByCol[col].push_back(row);
19 }
20 }
21 }
22
23 int totalCount = 0;
24
25 // Process each column
26 for (int col = 0; col < numCols; ++col) {
27 // Skip if column has no black pixels
28 if (blackPixelRowsByCol[col].empty()) {
29 continue;
30 }
31
32 // Get the first row with black pixel in this column
33 int firstRow = blackPixelRowsByCol[col][0];
34
35 // Skip if the first row doesn't have exactly 'target' black pixels
36 if (blackPixelsPerRow[firstRow] != target) {
37 continue;
38 }
39
40 int validBlackPixels = 0;
41
42 // Check if all rows with black pixels in this column are identical
43 // and the count matches the expected value
44 if (blackPixelRowsByCol[col].size() == blackPixelsPerRow[firstRow]) {
45 validBlackPixels = target;
46
47 // Verify all rows with black pixels in this column are identical
48 for (int currentRow : blackPixelRowsByCol[col]) {
49 if (picture[firstRow] != picture[currentRow]) {
50 validBlackPixels = 0;
51 break;
52 }
53 }
54 }
55
56 totalCount += validBlackPixels;
57 }
58
59 return totalCount;
60 }
61};
62
1/**
2 * Finds the number of black pixels that satisfy the lonely pixel rule
3 * @param picture - 2D array of 'B' (black) and 'W' (white) pixels
4 * @param target - The exact number of black pixels required in rows/columns
5 * @returns The count of black pixels meeting the criteria
6 */
7function findBlackPixel(picture: string[][], target: number): number {
8 const rowCount: number = picture.length;
9 const colCount: number = picture[0].length;
10
11 // Store row indices containing black pixels for each column
12 const blackPixelRowsByColumn: number[][] = Array.from({ length: colCount }, () => []);
13
14 // Count of black pixels in each row
15 const blackPixelsPerRow: number[] = Array(rowCount).fill(0);
16
17 // First pass: count black pixels and build column-to-row mappings
18 for (let row = 0; row < rowCount; row++) {
19 for (let col = 0; col < colCount; col++) {
20 if (picture[row][col] === 'B') {
21 blackPixelsPerRow[row]++;
22 blackPixelRowsByColumn[col].push(row);
23 }
24 }
25 }
26
27 let totalValidBlackPixels: number = 0;
28
29 // Second pass: check each column for valid black pixels
30 for (let col = 0; col < colCount; col++) {
31 // Skip if column has no black pixels or first row doesn't have target black pixels
32 if (blackPixelRowsByColumn[col].length === 0 ||
33 blackPixelsPerRow[blackPixelRowsByColumn[col][0]] !== target) {
34 continue;
35 }
36
37 const firstRowIndex: number = blackPixelRowsByColumn[col][0];
38 let validPixelsInColumn: number = 0;
39
40 // Check if all rows with black pixels in this column are identical
41 if (blackPixelRowsByColumn[col].length === blackPixelsPerRow[firstRowIndex]) {
42 validPixelsInColumn = target;
43
44 // Verify all rows with black pixels in this column have the same pattern
45 for (const currentRowIndex of blackPixelRowsByColumn[col]) {
46 if (picture[firstRowIndex].join('') !== picture[currentRowIndex].join('')) {
47 validPixelsInColumn = 0;
48 break;
49 }
50 }
51 }
52
53 totalValidBlackPixels += validPixelsInColumn;
54 }
55
56 return totalValidBlackPixels;
57}
58
Time and Space Complexity
Time Complexity: O(m × n²)
The algorithm consists of several parts:
- Initial traversal to count black pixels in each row and build column mappings:
O(m × n)
, where we iterate through allm
rows andn
columns once. - For each column
j
in dictionaryg
:- Checking if all rows with black pixels in column
j
are identical requires comparing each row with the first row - In the worst case, there could be
m
rows with black pixels in a column - Each row comparison takes
O(n)
time (comparingn
elements) - For each column, this could take up to
O(m × n)
time - Since there are at most
n
columns, the total time for this part isO(n × m × n) = O(m × n²)
- Checking if all rows with black pixels in column
The dominant operation is the row comparison step, giving us a total time complexity of O(m × n²)
.
Space Complexity: O(m × n)
The space usage includes:
rows
array:O(m)
space to store black pixel counts for each row- Dictionary
g
: In the worst case where every cell is black, we storem
row indices for each ofn
columns, resulting inO(m × n)
space - The
picture
matrix itself is passed as input and not counted in auxiliary space
Therefore, the total space complexity is O(m × n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Row Matching Condition
A frequent mistake is incorrectly interpreting what "all rows containing a black pixel in column c must be identical" means. Some developers might check if ALL rows in the grid are identical, or only check if the black pixels are in the same positions, rather than verifying that the entire row patterns match.
Incorrect approach:
# Wrong: Only checking if black pixels are in same positions
if all(picture[i2][col_idx] == "B" for i2 in column_to_row_indices[col_idx]):
total_valid_pixels += target
Correct approach:
# Right: Checking if entire rows are identical
if all(picture[row_idx] == picture[first_row_idx]
for row_idx in column_to_row_indices[col_idx]):
# Valid column
2. Off-by-One Error When Counting
Another pitfall is forgetting that when a column meets all conditions, we should add target
to the result (not 1
or len(column_to_row_indices[col_idx])
), because there are exactly target
lonely black pixels in that column.
Incorrect approach:
# Wrong: Adding 1 for each valid column if conditions_met: total_valid_pixels += 1 # This counts columns, not pixels!
Correct approach:
# Right: Adding target (number of black pixels in the column) if conditions_met: total_valid_pixels += target
3. Inefficient Row Comparison
When checking if rows are identical, comparing them element by element in nested loops creates unnecessary complexity. Python allows direct list comparison which is both cleaner and more efficient.
Inefficient approach:
# Unnecessarily complex row comparison
def rows_identical(row1_idx, row2_idx):
for j in range(len(picture[0])):
if picture[row1_idx][j] != picture[row2_idx][j]:
return False
return True
# Then checking all rows
all_identical = True
for row_idx in column_to_row_indices[col_idx]:
if not rows_identical(first_row_idx, row_idx):
all_identical = False
break
Efficient approach:
# Direct list comparison in Python
if all(picture[row_idx] == picture[first_row_idx]
for row_idx in column_to_row_indices[col_idx]):
# All rows are identical
4. Not Validating the Column Count Properly
The condition len(column_to_row_indices[col_idx]) == black_pixels_per_row[first_row_idx]
serves a dual purpose: it ensures the column has exactly target
black pixels AND that the count matches the row requirement. Missing this check or implementing it incorrectly will lead to wrong results.
Solution tip: Always remember that for a black pixel to be lonely, BOTH its row and column must have exactly target
black pixels, and this clever check validates both conditions simultaneously when combined with the row count check.
Which of the following array represent a max heap?
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!