Facebook Pixel

533. Lonely Pixel II 🔒

MediumArrayHash TableMatrix
Leetcode Link

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:

  1. Count condition: Both row r and column c must contain exactly target black pixels (including the pixel at (r, c) itself).

  2. Row matching condition: All rows that contain a black pixel in column c must be identical to row r. In other words, if column c 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Each of these identical rows has exactly target black pixels (since they're the same)
  2. Column j has exactly target black pixels (we know this)
  3. 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 add target 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:

  1. Row counting array rows: An array where rows[i] stores the count of black pixels in row i.

  2. Column-to-rows mapping g: A dictionary where g[j] contains a list of all row indices that have a black pixel in column j.

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):

  1. Get the first row i1 that has a black pixel in column j: i1 = g[j][0]

  2. Check if row i1 has exactly target black pixels:

    • If rows[i1] != target, skip this column as it can't contain lonely pixels
  3. Verify the two conditions for lonely pixels:

    • Check if the number of rows with black pixels in column j equals target: len(g[j]) == rows[i1]
      • Since rows[i1] = target, this ensures column j has exactly target black pixels
    • Check if all rows with black pixels in column j are identical to row i1: all(picture[i2] == picture[i1] for i2 in g[j])
  4. If both conditions are met, all black pixels in column j are lonely pixels, so we add target 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 exactly target 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 Evaluator

Example 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:

  1. Initial traversal to count black pixels in each row and build column mappings: O(m × n), where we iterate through all m rows and n columns once.
  2. For each column j in dictionary g:
    • 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 (comparing n 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 is O(n × m × n) = O(m × n²)

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:

  1. rows array: O(m) space to store black pixel counts for each row
  2. Dictionary g: In the worst case where every cell is black, we store m row indices for each of n columns, resulting in O(m × n) space
  3. 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following array represent a max heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More