Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

533. Lonely Pixel II - Leetcode Solution

Problem Description

Given a 2D array picture representing a black-and-white image, each element is either 'B' (black pixel) or 'W' (white pixel). A black pixel at location picture[i][j] is called a lonely pixel if:

  • Row i and column j both contain exactly N black pixels.
  • All rows that have a black pixel at column j are exactly the same as row i.
Your task is to return the number of black pixels that satisfy both conditions above.
Constraints:
  • Each row has the same number of columns.
  • The input is a 2D character array of 'B' and 'W'.
  • You are given an integer N.

Thought Process

At first glance, we might consider checking each black pixel in the matrix and verifying the two conditions directly. For each black pixel, we could count the number of black pixels in its row and column, and then compare all relevant rows for equality. However, this brute-force method would be inefficient, especially for large matrices.
To optimize, we must avoid redundant checks and repeated row comparisons. We can use hash maps to efficiently count rows and columns and to quickly check if rows are identical. By grouping identical rows and tracking black pixel counts, we can reduce unnecessary work and make the solution scalable.

Solution Approach

We can break the solution into several clear steps:

  1. Count black pixels in each row and column:
    • For each row, count the number of 'B's and store the string representation of the row.
    • For each column, count the number of 'B's.
  2. Group identical rows:
    • Use a hash map to group rows by their string content and track how many times each row appears.
  3. Check for valid rows:
    • For each unique row that has exactly N black pixels and occurs exactly N times, check its columns.
  4. Validate columns:
    • For each column with a 'B' in the candidate row, check if the column has exactly N black pixels.
    • If so, add N to the answer (since there are N identical rows, each with a 'B' in that column).

This approach ensures we only consider rows and columns that could possibly meet the conditions, and we use hash maps for efficient lookups and grouping.

Example Walkthrough

Example Input:

picture = [
  ['W','B','W','B','B','W'],
  ['W','B','W','B','B','W'],
  ['W','B','W','B','B','W'],
  ['W','W','B','W','B','W']
]
N = 3
    
Step-by-step:
  1. Count black pixels per row and column:
    • Row 0: 3 'B's, row string: "WBWBBW"
    • Row 1: 3 'B's, row string: "WBWBBW"
    • Row 2: 3 'B's, row string: "WBWBBW"
    • Row 3: 2 'B's, row string: "WWBWBW"
    • Column counts: [0, 3, 1, 3, 4, 0]
  2. Group identical rows:
    • "WBWBBW" occurs 3 times
    • "WWBWBW" occurs 1 time
  3. Check "WBWBBW" (since it has 3 'B's and occurs 3 times):
  4. For columns 1, 3, 4 (the 'B's in the row):
    • Column 1: 3 'B's (good)
    • Column 3: 3 'B's (good)
    • Column 4: 4 'B's (not good)
  5. So for columns 1 and 3, each is valid and appears in 3 rows, so answer = 3 + 3 = 6.

Output: 6

Time and Space Complexity

Brute-force Approach:

  • For each black pixel, check its row and column, and compare all relevant rows for equality.
  • Time Complexity: O(R * C * R) where R is number of rows and C is number of columns (since each black pixel could trigger a full row scan).
  • Space Complexity: O(1) extra (not counting input).
Optimized Approach:
  • Counting black pixels: O(R * C)
  • Grouping rows: O(R * C)
  • Final validation: O(R * C)
  • Overall Time Complexity: O(R * C)
  • Space Complexity: O(R * C) for storing row strings and counts.

The optimized solution is linear in the size of the matrix, making it efficient for large inputs.

Summary

We solved the Lonely Pixel II problem by grouping identical rows, counting black pixels in both rows and columns, and validating only those rows and columns that meet all conditions. Using hash maps for grouping and counting made the solution efficient and easy to understand. The key insight was to leverage the fact that all candidate rows must be identical, and that we can process the problem with a few passes over the data, leading to an elegant and scalable solution.

Code Implementation

class Solution:
    def findBlackPixel(self, picture: List[List[str]], N: int) -> int:
        from collections import Counter, defaultdict
        rows = [''.join(row) for row in picture]
        row_count = Counter(rows)
        m, n = len(picture), len(picture[0])
        col_count = [0] * n
        for i in range(m):
            for j in range(n):
                if picture[i][j] == 'B':
                    col_count[j] += 1
        res = 0
        for row_str, count in row_count.items():
            if count != N:
                continue
            idxs = [j for j, ch in enumerate(row_str) if ch == 'B']
            if len(idxs) != N:
                continue
            for j in idxs:
                if col_count[j] == N:
                    # Check all rows with 'B' in column j are the same as this row
                    valid = True
                    for i in range(m):
                        if picture[i][j] == 'B' and ''.join(picture[i]) != row_str:
                            valid = False
                            break
                    if valid:
                        res += N
        return res
      
class Solution {
public:
    int findBlackPixel(vector<vector<char>>& picture, int N) {
        int m = picture.size(), n = picture[0].size();
        vector<int> colCount(n, 0);
        unordered_map<string, int> rowMap;
        vector<string> rows(m);
        for (int i = 0; i < m; ++i) {
            string s(picture[i].begin(), picture[i].end());
            rows[i] = s;
            rowMap[s]++;
            for (int j = 0; j < n; ++j) {
                if (picture[i][j] == 'B') colCount[j]++;
            }
        }
        int res = 0;
        for (auto &kv : rowMap) {
            string rowStr = kv.first;
            int count = kv.second;
            if (count != N) continue;
            vector<int> idxs;
            for (int j = 0; j < n; ++j) {
                if (rowStr[j] == 'B') idxs.push_back(j);
            }
            if (idxs.size() != N) continue;
            for (int idx : idxs) {
                if (colCount[idx] == N) {
                    bool valid = true;
                    for (int i = 0; i < m; ++i) {
                        if (picture[i][idx] == 'B' && rows[i] != rowStr) {
                            valid = false;
                            break;
                        }
                    }
                    if (valid) res += N;
                }
            }
        }
        return res;
    }
};
      
class Solution {
    public int findBlackPixel(char[][] picture, int N) {
        int m = picture.length, n = picture[0].length;
        int[] colCount = new int[n];
        Map<String, Integer> rowMap = new HashMap<>();
        String[] rows = new String[m];
        for (int i = 0; i < m; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < n; ++j) {
                sb.append(picture[i][j]);
                if (picture[i][j] == 'B') colCount[j]++;
            }
            rows[i] = sb.toString();
            rowMap.put(rows[i], rowMap.getOrDefault(rows[i], 0) + 1);
        }
        int res = 0;
        for (String rowStr : rowMap.keySet()) {
            int count = rowMap.get(rowStr);
            if (count != N) continue;
            List<Integer> idxs = new ArrayList<>();
            for (int j = 0; j < n; ++j) {
                if (rowStr.charAt(j) == 'B') idxs.add(j);
            }
            if (idxs.size() != N) continue;
            for (int idx : idxs) {
                if (colCount[idx] == N) {
                    boolean valid = true;
                    for (int i = 0; i < m; ++i) {
                        if (picture[i][idx] == 'B' && !rows[i].equals(rowStr)) {
                            valid = false;
                            break;
                        }
                    }
                    if (valid) res += N;
                }
            }
        }
        return res;
    }
}
      
var findBlackPixel = function(picture, N) {
    const m = picture.length, n = picture[0].length;
    const colCount = Array(n).fill(0);
    const rowMap = new Map();
    const rows = [];
    for (let i = 0; i < m; ++i) {
        let rowStr = picture[i].join('');
        rows.push(rowStr);
        rowMap.set(rowStr, (rowMap.get(rowStr) || 0) + 1);
        for (let j = 0; j < n; ++j) {
            if (picture[i][j] === 'B') colCount[j]++;
        }
    }
    let res = 0;
    for (let [rowStr, count] of rowMap.entries()) {
        if (count !== N) continue;
        let idxs = [];
        for (let j = 0; j < n; ++j) {
            if (rowStr[j] === 'B') idxs.push(j);
        }
        if (idxs.length !== N) continue;
        for (let idx of idxs) {
            if (colCount[idx] === N) {
                let valid = true;
                for (let i = 0; i < m; ++i) {
                    if (picture[i][idx] === 'B' && rows[i] !== rowStr) {
                        valid = false;
                        break;
                    }
                }
                if (valid) res += N;
            }
        }
    }
    return res;
};