{x}
blog image

Stamping the Grid

You are given an m x n binary matrix grid where each cell is either 0 (empty) or 1 (occupied).

You are then given stamps of size stampHeight x stampWidth. We want to fit the stamps such that they follow the given restrictions and requirements:

  1. Cover all the empty cells.
  2. Do not cover any of the occupied cells.
  3. We can put as many stamps as we want.
  4. Stamps can overlap with each other.
  5. Stamps are not allowed to be rotated.
  6. Stamps must stay completely inside the grid.

Return true if it is possible to fit the stamps while following the given restrictions and requirements. Otherwise, return false.

 

Example 1:

Input: grid = [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], stampHeight = 4, stampWidth = 3
Output: true
Explanation: We have two overlapping stamps (labeled 1 and 2 in the image) that are able to cover all the empty cells.

Example 2:

Input: grid = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]], stampHeight = 2, stampWidth = 2 
Output: false 
Explanation: There is no way to fit the stamps onto all the empty cells without the stamps going outside the grid.

 

Constraints:

  • m == grid.length
  • n == grid[r].length
  • 1 <= m, n <= 105
  • 1 <= m * n <= 2 * 105
  • grid[r][c] is either 0 or 1.
  • 1 <= stampHeight, stampWidth <= 105

Solution Explanation: Stamping the Grid

This problem asks whether we can cover all empty cells in a grid using non-rotatable stamps of a fixed size without covering any occupied cells. The solution uses a clever combination of prefix sums and difference arrays to efficiently check this condition.

Approach:

  1. Prefix Sum Array: A 2D prefix sum array s is created. s[i][j] stores the total number of occupied cells (1s) in the subgrid from (0,0) to (i,j). This allows for O(1) calculation of the number of occupied cells in any rectangular subgrid.

  2. Iteration and Stamp Placement: The code iterates through the grid. For each cell (i,j), it checks if placing a stamp with its top-left corner at (i,j) is valid:

    • It calculates the number of occupied cells within the potential stamp area using the prefix sum array. If this count is 0 (all cells are empty), the stamp placement is valid.
    • If valid, a difference array d is updated. The difference array tracks the change in the number of stamp coverings for each cell. Adding a stamp increments the count at (i,j), decrements at the bottom and right edges, and increments again at the bottom-right corner. This is a standard technique for efficiently applying changes across a rectangular region.
  3. Difference Array to Coverage: After iterating through all possible stamp positions, the difference array d is converted to a coverage array by performing a prefix sum on d. d[i][j] now represents the total number of stamps covering cell (i,j).

  4. Validation: Finally, the code checks if all empty cells (0s in the original grid) have a non-zero coverage value in d. If any empty cell has a coverage of 0, it means it cannot be covered by any stamp, and the function returns false. Otherwise, it returns true.

Time and Space Complexity:

  • Time Complexity: O(m*n), where 'm' and 'n' are the dimensions of the grid. This is dominated by the nested loops for iterating through the grid and calculating prefix sums.
  • Space Complexity: O(m*n), due to the prefix sum array s and the difference array d.

Code Explanations (Python3 as Example):

The Python code implements the steps described above. The comments within the code provide a detailed explanation of each part. The other language implementations follow the same logic, with only syntax differences.

class Solution:
    def possibleToStamp(self, grid: List[List[int]], stampHeight: int, stampWidth: int) -> bool:
        m, n = len(grid), len(grid[0])
        s = [[0] * (n + 1) for _ in range(m + 1)] # Initialize prefix sum array
 
        #Calculate prefix sums
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + grid[i - 1][j - 1]
 
        d = [[0] * (n + 2) for _ in range(m + 2)] #Initialize difference array
 
        #Iterate and update difference array based on valid stamp placements
        for i in range(1, m - stampHeight + 2):
            for j in range(1, n - stampWidth + 2):
                x, y = i + stampHeight - 1, j + stampWidth - 1
                #Check if stamp placement is valid
                if s[x][y] - s[x][j - 1] - s[i - 1][y] + s[i - 1][j - 1] == 0:
                    d[i][j] += 1
                    d[i][y + 1] -= 1
                    d[x + 1][j] -= 1
                    d[x + 1][y + 1] += 1
 
        #Convert difference array to coverage array using prefix sum
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                d[i][j] += d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1]
 
        #Validate: Check if all empty cells have non-zero coverage
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if grid[i - 1][j - 1] == 0 and d[i][j] == 0:
                    return False
 
        return True

The other provided languages (Java, C++, Go, TypeScript, Rust, JavaScript, Kotlin) follow this same algorithmic structure. The differences are primarily in syntax and data structure handling specific to each language.