2661. First Completely Painted Row or Column
Problem Description
Given a 0-indexed integer array arr
and an m x n
integer matrix mat
, the task is to process arr
and paint the cells in mat
based on the sequence provided in arr
. Both arr
and mat
contain all integers in the range [1, m * n]
. The process involves iterating over arr
and painting the cell in mat
that corresponds to each integer arr[i]
. The goal is to determine the smallest index i
at which either a whole row or a whole column in the matrix mat
is completely painted. In other words, find the earliest point at which there's either a full row or a full column with all cells painted in the matrix.
Intuition
The solution revolves around tracking the painting progress in mat
as we process each element in arr
. The core thought is that we don't need to modify the matrix mat
itself but can instead track the number of painted cells in each row and column with auxiliary data structures. This leads us to the idea of using two arrays row
and col
to maintain the count of painted cells for each respective row and column.
Since we need to find the positions in the matrix mat
that correspond to the integers in arr
, we can set up a hash table idx
to map each number in the matrix to its coordinate (i, j)
. This allows us to quickly access the correct row and column to increment our counts.
With this setup, we iterate through arr
, using the hashed mapping to find where arr[k]
should be painted in mat
. Each time we increment the counts in row
and col
, we check if either of them has reached the size of their respective dimension (n for rows, m for columns). If they have, it means that a complete row or column has been painted, and we can return the current index k
as the result. This approach leads to an efficient solution since it avoids the need to modify the matrix directly and leverages fast lookups and updates in the auxiliary arrays.
Solution Approach
The implementation consists of several key steps that use algorithms and data structures to efficiently solve the problem.
Firstly, we create a hash table named idx
to store the position of each element in the matrix mat
. For each element in mat
, we make an entry in idx
where the key is the element mat[i][j]
and the value is a tuple (i, j)
representing its row and column indices.
idx = {}
for i in range(m):
for j in range(n):
idx[mat[i][j]] = (i, j)
This hash table allows us to have constant-time lookups for the position of any element later when we traverse arr
.
Next, we define two arrays, row
and col
, with lengths corresponding to the number of rows m
and the number of columns n
in the matrix mat
. These arrays are used to count how many cells have been painted in each row and column, respectively.
row = [0] * m col = [0] * n
The main part of the solution involves iterating through the array arr
. For each element arr[k]
, we get its corresponding position (i, j)
in the matrix mat
using the hash table idx
.
for k in range(len(arr)):
i, j = idx[arr[k]]
We increment the counts in row[i]
and col[j]
since arr[k]
marks the cell at (i, j)
as painted.
row[i] += 1 col[j] += 1
Then, we check if we have completed painting a row or a column. In other words, if either row[i]
equals the number of columns n
or col[j]
equals the number of rows m
, we have found our solution. At that point, we can return the current index k
as this is the first index where a row or column is completely painted.
if row[i] == n or col[j] == m: return k
This approach efficiently determines the solution by using a hash table for fast lookups and simple arrays for counting, thus avoiding the need to make any modifications to the matrix mat
itself.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution with a small example. Assume we are given the following matrix mat
and array arr
:
mat = [ [1, 2], [3, 4] ] arr = [3, 1, 4, 2]
The matrix mat
is of size 2 x 2
, so m = 2
and n = 2
. Our goal is to paint the cells in mat
in the order specified by arr
and find the smallest index i
at which either a whole row or a whole column is completely painted.
Step 1: Create a hash table idx
mapping matrix values to coordinates:
idx = {1: (0, 0), 2: (0, 1), 3: (1, 0), 4: (1, 1)}
Step 2: Initialize counts for rows and columns:
row = [0, 0] col = [0, 0]
Step 3: Start iterating through arr
:
- For
arr[0] = 3
, the position inmat
isidx[3] = (1, 0)
. Incrementrow[1]
andcol[0]
. Nowrow = [0, 1]
,col = [1, 0]
. - For
arr[1] = 1
, the position inmat
isidx[1] = (0, 0)
. Incrementrow[0]
andcol[0]
. Nowrow = [1, 1]
,col = [2, 0]
.
Since col[0]
is now equal to the number of rows m
, we've painted an entire column. The smallest index at this point is i = 1
.
So, the earliest index in arr
at which a whole row or column is painted is 1
.
Solution Implementation
1class Solution:
2 def first_complete_index(self, sequence: List[int], matrix: List[List[int]]) -> int:
3 # Get the dimensions of the matrix.
4 rows_count, cols_count = len(matrix), len(matrix[0])
5
6 # Create a dictionary to map each number to its position in the matrix.
7 number_position_index = {}
8 for row_index in range(rows_count):
9 for col_index in range(cols_count):
10 number = matrix[row_index][col_index]
11 number_position_index[number] = (row_index, col_index)
12
13 # Initialize counters for each row and column.
14 row_counters = [0] * rows_count
15 col_counters = [0] * cols_count
16
17 # Iterate through the sequence and update row and column counters.
18 for index, number in enumerate(sequence):
19 if number in number_position_index:
20 row_index, col_index = number_position_index[number]
21 row_counters[row_index] += 1
22 col_counters[col_index] += 1
23
24 # Check if a row or a column is complete.
25 if row_counters[row_index] == cols_count or col_counters[col_index] == rows_count:
26 return index
27
28 # If no row or column has been completed, return -1 as a default case.
29 return -1
30
31# The List type needs to be imported from the typing module.
32from typing import List
33
34# This would then allow you to use the Solution class and its method.
35# Example usage:
36# solution_instance = Solution()
37# result = solution_instance.first_complete_index(sequence, matrix)
38
1class Solution {
2
3 /**
4 * Finds the first index in the input array where a complete row or column is found in the input matrix.
5 * @param arr Single-dimensional array of integers.
6 * @param mat Two-dimensional matrix of integers.
7 * @return The earliest index at which the input array causes a row or a column in the matrix to be filled.
8 */
9 public int firstCompleteIndex(int[] arr, int[][] mat) {
10 // Dimensions of the matrix
11 int rowCount = mat.length;
12 int colCount = mat[0].length;
13
14 // Mapping from the value to its coordinates in the matrix
15 Map<Integer, int[]> valueToIndexMap = new HashMap<>();
16 for (int row = 0; row < rowCount; ++row) {
17 for (int col = 0; col < colCount; ++col) {
18 valueToIndexMap.put(mat[row][col], new int[]{row, col});
19 }
20 }
21
22 // Arrays to keep track of the number of values found per row and column
23 int[] rowCompletion = new int[rowCount];
24 int[] colCompletion = new int[colCount];
25
26 // Iterate through the array `arr`
27 for (int k = 0; ; ++k) {
28 // Get the coordinates of the current array value in the matrix
29 int[] coordinates = valueToIndexMap.get(arr[k]);
30 int rowIndex = coordinates[0];
31 int colIndex = coordinates[1];
32
33 // Increment the counters for the row and column
34 rowCompletion[rowIndex]++;
35 colCompletion[colIndex]++;
36
37 // Check if the current row or column is completed
38 if (rowCompletion[rowIndex] == colCount || colCompletion[colIndex] == rowCount) {
39 // Return the current index if a row or column is complete
40 return k;
41 }
42 }
43 }
44}
45
1#include <vector>
2#include <unordered_map>
3
4class Solution {
5public:
6 // Returns the first index at which all numbers in a row or column are filled
7 // according to the order given in 'arr'.
8 int firstCompleteIndex(vector<int>& order, vector<vector<int>>& matrix) {
9 int rows = matrix.size(), cols = matrix[0].size();
10 unordered_map<int, pair<int, int>> numberToPosition;
11
12 // Populate a hash map with the number as the key and its position (i, j)
13 // in the matrix as the value.
14 for (int i = 0; i < rows; ++i) {
15 for (int j = 0; j < cols; ++j) {
16 numberToPosition[matrix[i][j]] = {i, j};
17 }
18 }
19
20 // Create a vector to keep track of the count of filled numbers in each row and column.
21 vector<int> rowCount(rows, 0), colCount(cols, 0);
22
23 // Iterate through the order vector to simulate filling the matrix.
24 for (int k = 0; k < order.size(); ++k) {
25 // Get the position of the current number in the order array
26 // from the hash map.
27 auto [i, j] = numberToPosition[order[k]];
28
29 // Increment the filled numbers count for the respective row and column.
30 ++rowCount[i];
31 ++colCount[j];
32
33 // If a row or a column is completely filled, return the current index.
34 if (rowCount[i] == cols || colCount[j] == rows) {
35 return k;
36 }
37 }
38
39 // The code prior guarantees a result, so this return statement might never be reached.
40 // However, it is here as a fail-safe.
41 return -1;
42 }
43};
44
1// Function to find the first index at which all numbers in either the
2// same row or the same column of the matrix have appeared in the array.
3function firstCompleteIndex(arr: number[], mat: number[][]): number {
4 // Get the dimensions of the matrix
5 const rowCount = mat.length;
6 const colCount = mat[0].length;
7
8 // Map to store the position for each value in the matrix
9 const positionMap: Map<number, number[]> = new Map();
10 // Fill the map with positions of each value
11 for (let row = 0; row < rowCount; ++row) {
12 for (let col = 0; col < colCount; ++col) {
13 positionMap.set(mat[row][col], [row, col]);
14 }
15 }
16
17 // Arrays to keep track of the count of numbers found in each row and column
18 const rowCompletionCount: number[] = new Array(rowCount).fill(0);
19 const colCompletionCount: number[] = new Array(colCount).fill(0);
20
21 // Iterate through the array elements to find the complete row/col index
22 for (let index = 0; index < arr.length; ++index) {
23 // Get the position of the current element from the map
24 const [row, col] = positionMap.get(arr[index])!;
25 // Increment completion count for the row and column
26 ++rowCompletionCount[row];
27 ++colCompletionCount[col];
28
29 // Check if the current row or column is completed
30 if (rowCompletionCount[row] === colCount || colCompletionCount[col] === rowCount) {
31 // Return the current index if a complete row or column is found
32 return index;
33 }
34 }
35
36 // In case no complete row or column is found,
37 // the function will keep running indefinitely due to the lack of a stopping condition in the for loop.
38}
39
Time and Space Complexity
The time complexity of the given code can be broken down into two main parts. Firstly, populating the idx
dictionary requires iterating through each element of the m x n
matrix, which takes O(m * n)
time. Secondly, iterating over the arr
array with k
elements and updating the row
and col
counts takes O(k)
time. However, each element's index is accessed in constant time due to the dictionary. Therefore, the overall time complexity is the sum of both parts, O(m * n + k)
.
The space complexity involves the storage used by the idx
dictionary, which holds one entry for each element in the m x n
matrix, hence O(m * n)
space. Additionally, the row
and col
arrays utilize O(m)
and O(n)
space, respectively. Consequently, the total space complexity is O(m * n)
since O(m + n)
is subsumed under O(m * n)
when m
and n
are of the same order.
Learn more about how to find time and space complexity quickly using problem constraints.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
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!