Prerequisites

Before attempting this problem, you should be comfortable with:

  • 2D Arrays/Matrices - Iterating through rows and columns
  • Counting Arrays - Precomputing row and column counts for efficient lookups
  • Two-Pass Algorithms - First pass to gather statistics, second pass to compute results

1. Counting with Arrays

Intuition

A pixel is "lonely" if it's the only black pixel in both its row and its column. Instead of checking the entire row and column for each black pixel (which would be slow), we can precompute the count of black pixels in every row and every column. Then, a black pixel at position (i, j) is lonely if and only if both row_count[i] and column_count[j] equal 1.

Algorithm

  1. Create two arrays: row_count of size n (number of rows) and column_count of size m (number of columns), initialized to zero.
  2. First pass: iterate through the entire grid. For each black pixel ('B') at (i, j), increment row_count[i] and column_count[j].
  3. Second pass: iterate through the grid again. For each black pixel at (i, j), check if row_count[i] == 1 and column_count[j] == 1. If both conditions hold, increment the answer.
  4. Return the total count of lonely pixels.
class Solution:
    def findLonelyPixel(self, picture: List[List[str]]) -> int:
        n = len(picture)
        m = len(picture[0])
        
        # Arrays to store the count of black cells in rows and columns.
        row_count = [0] * n
        column_count = [0] * m
        for i in range(n):
            for j in range(m):
                if picture[i][j] == 'B':
                    row_count[i] += 1
                    column_count[j] += 1
        
        answer = 0
        for i in range(n):
            for j in range(m):
                # Its a lonely cell, if the current cell is black and,
                # the count of black cells in its row and column is 1.
                if picture[i][j] == 'B' and row_count[i] == 1 and column_count[j] == 1:
                    answer += 1
        
        return answer

Time & Space Complexity

  • Time complexity: O(MN)O(M \cdot N)
  • Space complexity: O(M+N)O(M + N)

Where MM is the number of rows in the given matrix picture, and NN is the number of columns in it.


2. Space Optimized Counting

Intuition

We can avoid using extra arrays by reusing the first row and first column of the grid itself to store the counts. However, we must first handle lonely pixels in the first row and first column separately, since those cells will be overwritten. After that, we convert the border cells to store counts and use them to check interior cells.

Algorithm

  1. First, check for lonely pixels in the first row and first column using a helper function that scans the entire row and column.
  2. Convert the first row and first column from 'B'/'W' to '0'/'1' to use as counters.
  3. Iterate through the interior cells (excluding first row and column). For each black pixel, increment the count stored in the corresponding first row and first column cells.
  4. Finally, scan the interior again. A black pixel at (i, j) is lonely if both picture[0][j] and picture[i][0] equal '1'.
  5. Return the total count.
class Solution:
    def findLonelyPixel(self, picture: List[List[str]]) -> int:
        # Returns true if the cell at (x, y) is lonely.
        # There should not be any other black cell 
        # In the first row and column except (x, y) itself.
        def check(x, y):
            n = len(picture)
            m = len(picture[0])
            
            cnt = 0
            for i in range(n):
                cnt += 1 if picture[i][y] == 'B' else 0
            
            for j in range(m):
                # avoid double count (x, y)
                if j != y:
                    cnt += 1 if picture[x][j] == 'B' else 0
            
            return picture[x][y] == 'B' and cnt == 1
        
        n = len(picture)
        m = len(picture[0])
        
        answer = 0
        for j in range(m):
            answer += 1 if check(0, j) else 0
        
        for i in range(1, n):
            answer += 1 if check(i, 0) else 0
        
        # Convert cell 'B' to '1' and 'W' to '0'
        for j in range(m):
            picture[0][j] = '1' if picture[0][j] == 'B' else '0'
        
        for i in range(n):
            picture[i][0] = '1' if picture[i][0] == 'B' else '0'
        
        # If the cell is black increment the count of corresponding row and column by 1
        for i in range(1, n):
            for j in range(1, m):
                if picture[i][j] == 'B':
                    picture[i][0] = chr(ord(picture[i][0]) + 1)
                    picture[0][j] = chr(ord(picture[0][j]) + 1)
        
        for i in range(1, n):
            for j in range(1, m):
                if picture[i][j] == 'B':
                    if picture[0][j] == '1' and picture[i][0] == '1':
                        answer += 1
        
        return answer

Time & Space Complexity

  • Time complexity: O(MN)O(M \cdot N)
  • Space complexity: O(1)O(1) constant space

Where MM is the number of rows in the given matrix picture, and NN is the number of columns in it.

Common Pitfalls

Checking Only the Row or Only the Column

A common mistake is to count a black pixel as lonely if it is the only black pixel in its row, without also verifying that it is the only black pixel in its column (or vice versa). Both conditions must be satisfied simultaneously for a pixel to be considered lonely.

Confusing Row and Column Indices

When working with 2D matrices, it is easy to mix up row and column indices. Remember that picture[i][j] refers to row i and column j. Swapping these when updating or querying counts will produce incorrect results.

Forgetting to Handle Edge Cases with No Black Pixels

If the matrix contains no black pixels at all, the answer is 0. While most implementations handle this naturally, some solutions that assume at least one black pixel exists may encounter issues or unnecessary iterations.