2201. Count Artifacts That Can Be Extracted
Problem Description
You have an n x n
grid (0-indexed) that contains buried artifacts. Each artifact is a rectangular region within the grid.
Input:
n
: The size of the grid (n × n)artifacts
: A 2D array where each element[r1, c1, r2, c2]
represents one artifact(r1, c1)
is the top-left corner of the artifact(r2, c2)
is the bottom-right corner of the artifact
dig
: A 2D array where each element[r, c]
represents a cell you will excavate
Task:
You need to excavate cells according to the dig
array. When you excavate a cell, you remove the mud from it. If an artifact has all of its cells excavated (all mud removed), you can extract that artifact. Your goal is to count how many artifacts can be fully extracted.
Key Points:
- An artifact can only be extracted if ALL cells it occupies have been excavated
- No two artifacts overlap with each other
- Each artifact covers at most 4 cells
- All coordinates in
dig
are unique (no duplicate excavations)
Example:
If an artifact occupies cells [1,1]
to [2,2]
(a 2×2 square), you must excavate all four cells (1,1)
, (1,2)
, (2,1)
, and (2,2)
to extract this artifact.
The solution uses a hash set to store all excavated positions from the dig
array. Then for each artifact, it checks if all cells within the artifact's rectangular region have been excavated. The function returns the count of artifacts that can be fully extracted.
Intuition
The core challenge is determining whether we've excavated all cells that belong to each artifact. Since we need to check if specific cells have been excavated, we need a fast way to look up whether a cell (r, c)
has been dug or not.
A hash set is perfect for this - we can store all excavated positions in O(1) lookup time. We convert the dig
list into a set of tuples {(i, j) for i, j in dig}
for instant membership checking.
For each artifact defined by corners (r1, c1)
and (r2, c2)
, we need to verify that every single cell within this rectangular region has been excavated. This means checking all cells where r
ranges from r1
to r2
(inclusive) and c
ranges from c1
to c2
(inclusive).
The key insight is that an artifact can only be extracted if 100% of its cells are excavated - even one missing cell means we cannot extract it. So for each artifact, we iterate through all its cells and check if they're all in our excavated set. If they are, we increment our count.
Since artifacts don't overlap and each covers at most 4 cells, we can independently check each artifact without worrying about complex interactions between them. The solution becomes straightforward: convert excavations to a set for fast lookup, then for each artifact, check if all its cells have been excavated.
Solution Approach
The solution uses a hash table approach to efficiently track excavated cells and check artifact extraction eligibility.
Step 1: Create a Hash Set of Excavated Cells
First, we convert the dig
list into a set for O(1) lookup operations:
s = {(i, j) for i, j in dig}
This creates a set containing all excavated cell coordinates as tuples.
Step 2: Define a Helper Function to Check Individual Artifacts
The check
function determines if a single artifact can be extracted:
def check(a: List[int]) -> bool:
x1, y1, x2, y2 = a
return all(
(x, y) in s for x in range(x1, x2 + 1) for y in range(y1, y2 + 1)
)
This function:
- Unpacks the artifact boundaries
[x1, y1, x2, y2]
- Iterates through all cells in the rectangular region from
(x1, y1)
to(x2, y2)
inclusive - Uses the
all()
function to verify every cell(x, y)
in this region exists in our excavated sets
- Returns
True
only if all cells have been excavated
Step 3: Count Extractable Artifacts
Finally, we count how many artifacts can be extracted:
return sum(check(a) for a in artifacts)
This uses a generator expression with sum()
to:
- Iterate through each artifact in the
artifacts
list - Apply the
check
function to each artifact - Sum up the boolean results (
True
= 1,False
= 0) to get the total count
Time Complexity: O(m + k) where m is the number of excavated cells and k is the total number of cells covered by all artifacts (at most 4 cells per artifact).
Space Complexity: O(m) for storing the hash set of excavated positions.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Given:
n = 3
(3×3 grid)artifacts = [[0,0,0,1], [1,1,2,2]]
- Artifact 1: occupies cells (0,0) and (0,1)
- Artifact 2: occupies cells (1,1), (1,2), (2,1), (2,2)
dig = [[0,0], [0,1], [1,1], [2,1]]
Step 1: Create Hash Set of Excavated Cells
Convert dig
into a set:
s = {(0,0), (0,1), (1,1), (2,1)}
Step 2: Check Each Artifact
For Artifact 1 [0,0,0,1]
:
- Need to check cells from (0,0) to (0,1)
- Check (0,0): ✓ in set
- Check (0,1): ✓ in set
- All cells excavated → Can extract!
For Artifact 2 [1,1,2,2]
:
- Need to check cells from (1,1) to (2,2)
- Check (1,1): ✓ in set
- Check (1,2): ✗ NOT in set
- Since (1,2) is missing, cannot extract this artifact
Step 3: Count Extractable Artifacts
- Artifact 1: Extractable (returns True → 1)
- Artifact 2: Not extractable (returns False → 0)
- Total: 1 + 0 = 1
Result: We can extract 1 artifact.
The key insight here is that even though we excavated 3 out of 4 cells for Artifact 2, we still cannot extract it because extraction requires ALL cells to be excavated. Only Artifact 1, with both of its cells excavated, can be successfully extracted.
Solution Implementation
1class Solution:
2 def digArtifacts(
3 self, n: int, artifacts: List[List[int]], dig: List[List[int]]
4 ) -> int:
5 """
6 Count the number of artifacts that can be fully excavated.
7
8 Args:
9 n: Size of the n x n grid
10 artifacts: List of artifacts, each defined by [r1, c1, r2, c2] coordinates
11 dig: List of cells that have been excavated, each as [row, col]
12
13 Returns:
14 Number of artifacts that are completely excavated
15 """
16
17 def is_artifact_fully_excavated(artifact: List[int]) -> bool:
18 """
19 Check if an artifact is fully excavated by verifying all its cells are dug.
20
21 Args:
22 artifact: Artifact coordinates [row1, col1, row2, col2]
23
24 Returns:
25 True if all cells of the artifact have been excavated, False otherwise
26 """
27 row1, col1, row2, col2 = artifact
28
29 # Check if every cell within the artifact's rectangular area has been excavated
30 for row in range(row1, row2 + 1):
31 for col in range(col1, col2 + 1):
32 if (row, col) not in excavated_cells:
33 return False
34 return True
35
36 # Convert the list of excavated cells to a set for O(1) lookup
37 excavated_cells = {(row, col) for row, col in dig}
38
39 # Count how many artifacts are fully excavated
40 fully_excavated_count = sum(
41 is_artifact_fully_excavated(artifact) for artifact in artifacts
42 )
43
44 return fully_excavated_count
45
1class Solution {
2 // Set to store all excavated cell positions (encoded as single integers)
3 private Set<Integer> excavatedCells = new HashSet<>();
4 // Grid dimension
5 private int gridSize;
6
7 /**
8 * Counts the number of artifacts that can be completely extracted after digging.
9 * @param n - The size of the n x n grid
10 * @param artifacts - Array of artifacts, each defined by [r1, c1, r2, c2] coordinates
11 * @param dig - Array of cells that have been excavated, each defined by [row, col]
12 * @return The number of artifacts that can be completely extracted
13 */
14 public int digArtifacts(int n, int[][] artifacts, int[][] dig) {
15 this.gridSize = n;
16
17 // Store all excavated positions in a set for O(1) lookup
18 // Encode 2D position (row, col) as a single integer: row * n + col
19 for (int[] position : dig) {
20 excavatedCells.add(position[0] * gridSize + position[1]);
21 }
22
23 // Count artifacts that are fully excavated
24 int fullyExcavatedCount = 0;
25 for (int[] artifact : artifacts) {
26 fullyExcavatedCount += isFullyExcavated(artifact);
27 }
28
29 return fullyExcavatedCount;
30 }
31
32 /**
33 * Checks if an artifact is fully excavated.
34 * @param artifact - The artifact boundaries [r1, c1, r2, c2]
35 * @return 1 if the artifact is fully excavated, 0 otherwise
36 */
37 private int isFullyExcavated(int[] artifact) {
38 int topRow = artifact[0];
39 int leftCol = artifact[1];
40 int bottomRow = artifact[2];
41 int rightCol = artifact[3];
42
43 // Check if all cells covered by the artifact have been excavated
44 for (int row = topRow; row <= bottomRow; row++) {
45 for (int col = leftCol; col <= rightCol; col++) {
46 // If any cell is not excavated, the artifact cannot be extracted
47 if (!excavatedCells.contains(row * gridSize + col)) {
48 return 0;
49 }
50 }
51 }
52
53 // All cells of the artifact have been excavated
54 return 1;
55 }
56}
57
1class Solution {
2public:
3 int digArtifacts(int n, vector<vector<int>>& artifacts, vector<vector<int>>& dig) {
4 // Store all excavated cells in a hash set for O(1) lookup
5 // Convert 2D coordinates to 1D by formula: row * n + col
6 unordered_set<int> excavatedCells;
7 for (auto& digPosition : dig) {
8 int cellIndex = digPosition[0] * n + digPosition[1];
9 excavatedCells.insert(cellIndex);
10 }
11
12 // Lambda function to check if an artifact is fully excavated
13 auto isArtifactFullyExcavated = [&](vector<int>& artifact) {
14 int topRow = artifact[0];
15 int leftCol = artifact[1];
16 int bottomRow = artifact[2];
17 int rightCol = artifact[3];
18
19 // Check every cell covered by this artifact
20 for (int row = topRow; row <= bottomRow; ++row) {
21 for (int col = leftCol; col <= rightCol; ++col) {
22 int cellIndex = row * n + col;
23 // If any cell is not excavated, artifact is not fully uncovered
24 if (!excavatedCells.count(cellIndex)) {
25 return 0;
26 }
27 }
28 }
29 // All cells of the artifact have been excavated
30 return 1;
31 };
32
33 // Count the number of fully excavated artifacts
34 int fullyExcavatedCount = 0;
35 for (auto& artifact : artifacts) {
36 fullyExcavatedCount += isArtifactFullyExcavated(artifact);
37 }
38
39 return fullyExcavatedCount;
40 }
41};
42
1/**
2 * Counts the number of artifacts that can be fully excavated
3 * @param n - The size of the n x n grid
4 * @param artifacts - Array of artifacts, each defined by [r1, c1, r2, c2] coordinates
5 * @param dig - Array of cells that have been excavated, each defined by [row, col]
6 * @returns The number of artifacts that have been completely excavated
7 */
8function digArtifacts(n: number, artifacts: number[][], dig: number[][]): number {
9 // Create a set to store all excavated cell positions as encoded values
10 // Each cell (x, y) is encoded as x * n + y for efficient lookup
11 const excavatedCells: Set<number> = new Set<number>();
12
13 // Populate the set with all excavated cell positions
14 for (const [row, col] of dig) {
15 excavatedCells.add(row * n + col);
16 }
17
18 // Counter for fully excavated artifacts
19 let fullyExcavatedCount: number = 0;
20
21 /**
22 * Checks if an artifact has been fully excavated
23 * @param artifact - The artifact defined by [r1, c1, r2, c2] coordinates
24 * @returns 1 if fully excavated, 0 otherwise
25 */
26 const isFullyExcavated = (artifact: number[]): number => {
27 const [row1, col1, row2, col2] = artifact;
28
29 // Check every cell within the artifact's rectangular area
30 for (let row = row1; row <= row2; row++) {
31 for (let col = col1; col <= col2; col++) {
32 // If any cell is not excavated, the artifact is not fully excavated
33 if (!excavatedCells.has(row * n + col)) {
34 return 0;
35 }
36 }
37 }
38
39 // All cells of the artifact have been excavated
40 return 1;
41 };
42
43 // Check each artifact and count how many are fully excavated
44 for (const artifact of artifacts) {
45 fullyExcavatedCount += isFullyExcavated(artifact);
46 }
47
48 return fullyExcavatedCount;
49}
50
Time and Space Complexity
Time Complexity: O(m + k)
The time complexity consists of two main parts:
- Creating the set
s
from thedig
list takesO(k)
time, wherek
is the number of excavated cells (length ofdig
) - For each artifact in
artifacts
, thecheck
function iterates through all cells within the artifact's rectangular area. In the worst case, an artifact could span the entiren×n
grid, making each checkO(n²)
. However, the total number of cells across all artifacts is bounded. Since we're checking membership in a set (which isO(1)
per lookup), and the total cells checked across all artifacts is proportional tom
(the total area covered by artifacts), the iteration through all artifacts takesO(m)
time
Therefore, the total time complexity is O(m + k)
.
Space Complexity: O(k)
The space complexity is determined by:
- The set
s
which stores all excavated cells fromdig
, requiringO(k)
space wherek
is the number of excavated cells - The generator expression in the return statement doesn't create additional data structures, it evaluates lazily
- The
check
function uses only a constant amount of additional space for variables
Therefore, the space complexity is O(k)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Boundary Iteration
One of the most common mistakes is forgetting that the artifact boundaries are inclusive. When iterating through the cells of an artifact from (r1, c1)
to (r2, c2)
, you must include the end coordinates.
Wrong approach:
# Missing the +1, which excludes the boundary cells
for row in range(row1, row2): # ❌ Excludes row2
for col in range(col1, col2): # ❌ Excludes col2
Correct approach:
# Include +1 to make the range inclusive of the end coordinates
for row in range(row1, row2 + 1): # ✓ Includes row2
for col in range(col1, col2 + 1): # ✓ Includes col2
2. Using List Instead of Set for Excavated Cells
Checking membership in a list has O(n) time complexity, which can significantly slow down the solution when there are many excavated cells.
Inefficient approach:
excavated_cells = dig # ❌ Still a list # or excavated_cells = [(row, col) for row, col in dig] # ❌ Still a list # Checking membership in a list is O(n) for each lookup if [row, col] in excavated_cells: # Slow!
Efficient approach:
excavated_cells = {(row, col) for row, col in dig} # ✓ Set with O(1) lookup
3. Tuple vs List Confusion in Set
When storing coordinates in a set, they must be hashable (immutable). Lists are not hashable, but tuples are.
Wrong approach:
excavated_cells = {[row, col] for row, col in dig} # ❌ Lists are not hashable # This will raise: TypeError: unhashable type: 'list'
Correct approach:
excavated_cells = {(row, col) for row, col in dig} # ✓ Tuples are hashable
4. Early Return Optimization Mistake
When checking if an artifact is fully excavated, returning True
as soon as you find one excavated cell is incorrect. You need ALL cells to be excavated.
Wrong logic:
def is_artifact_fully_excavated(artifact):
row1, col1, row2, col2 = artifact
for row in range(row1, row2 + 1):
for col in range(col1, col2 + 1):
if (row, col) in excavated_cells:
return True # ❌ Wrong! Just one cell isn't enough
return False
Correct logic:
def is_artifact_fully_excavated(artifact):
row1, col1, row2, col2 = artifact
for row in range(row1, row2 + 1):
for col in range(col1, col2 + 1):
if (row, col) not in excavated_cells:
return False # ✓ Return False if ANY cell is not excavated
return True # ✓ Return True only if ALL cells are excavated
What are the most two important steps in writing a depth first search function? (Select 2)
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!