2125. Number of Laser Beams in a Bank
Problem Description
You are given a 2D floor plan of a bank represented as a binary string array called bank
. Each string in the array represents a row of the floor plan, where '0'
means an empty cell and '1'
means there's a security device in that cell.
The security system creates laser beams between security devices according to these rules:
- A laser beam connects two security devices only if they are on different rows
- For a beam to exist between devices on row
r1
and rowr2
(wherer1 < r2
), all rows between them must have no security devices
Each pair of devices that meets these conditions creates exactly one laser beam, and beams don't interfere with each other.
Your task is to count the total number of laser beams in the bank.
For example, if you have security devices on row 0 and row 2, with row 1 being empty, then each device on row 0 forms a beam with each device on row 2. If row 0 has 3 devices and row 2 has 2 devices, there would be 3 × 2 = 6
laser beams between these two rows.
The key insight is that laser beams only form between consecutive rows that have security devices (ignoring empty rows in between). So you need to find each pair of non-empty rows and multiply their device counts to get the number of beams between them.
Intuition
Let's think about when laser beams actually form. If we have security devices on multiple rows, beams only connect devices between "adjacent" rows that have devices (where "adjacent" means no other rows with devices in between, though there can be empty rows).
Consider this example: if row 0 has devices, row 1 is empty, row 2 has devices, row 3 is empty, and row 4 has devices. The beams would form between:
- Row 0 and Row 2 (since row 1 is empty)
- Row 2 and Row 4 (since row 3 is empty)
There would be NO beams between row 0 and row 4 because row 2 has devices in between them.
This means we can simplify the problem: we only need to consider consecutive non-empty rows. For each pair of consecutive non-empty rows, the number of beams is simply the product of the number of devices in each row.
The algorithm becomes straightforward:
- Skip any empty rows (rows with no
'1'
s) - When we find a row with devices, multiply its device count by the previous non-empty row's device count
- Add this product to our total
- Update our "previous row" count to the current row's count for the next iteration
This works because we're essentially tracking pairs of consecutive non-empty rows and calculating devices_in_row1 × devices_in_row2
for each pair, which gives us the total number of beams between those rows.
Learn more about Math patterns.
Solution Approach
The solution uses a simple row-by-row counting approach with two key variables:
ans
: accumulates the total number of laser beamspre
: stores the count of security devices from the previous non-empty row
Here's the step-by-step implementation:
-
Initialize variables: Start with
ans = 0
(total beams) andpre = 0
(no previous row initially). -
Iterate through each row: For each row in the bank:
- Count the number of
'1'
s in the current row usingrow.count("1")
and store it incur
- If
cur > 0
(the row has security devices):- Calculate beams between this row and the previous non-empty row:
pre * cur
- Add these beams to the total:
ans += pre * cur
- Update
pre = cur
for the next iteration
- Calculate beams between this row and the previous non-empty row:
- Count the number of
-
Return the result: After processing all rows,
ans
contains the total number of laser beams.
The key insight is that we only update pre
when we encounter a non-empty row, effectively skipping empty rows. This ensures we're always calculating beams between consecutive non-empty rows.
For example, with rows having [3, 0, 2, 0, 1] devices:
- Row 0 (3 devices):
ans = 0 + 0*3 = 0
,pre = 3
- Row 1 (0 devices): skip
- Row 2 (2 devices):
ans = 0 + 3*2 = 6
,pre = 2
- Row 3 (0 devices): skip
- Row 4 (1 device):
ans = 6 + 2*1 = 8
,pre = 1
- Final answer: 8 beams
The time complexity is O(m*n)
where m
is the number of rows and n
is the length of each row (for counting '1'
s). The space complexity is O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this bank floor plan:
bank = ["011", "000", "101", "001"]
This represents:
- Row 0: "011" → 2 security devices
- Row 1: "000" → 0 security devices (empty)
- Row 2: "101" → 2 security devices
- Row 3: "001" → 1 security device
Step-by-step execution:
-
Initialize:
ans = 0
,pre = 0
-
Process Row 0 ("011"):
- Count devices:
cur = 2
(two '1's) - Since
cur > 0
:- Calculate beams:
pre * cur = 0 * 2 = 0
- Update total:
ans = 0 + 0 = 0
- Update previous:
pre = 2
- Calculate beams:
- Count devices:
-
Process Row 1 ("000"):
- Count devices:
cur = 0
(no '1's) - Since
cur = 0
, skip this row pre
remains 2,ans
remains 0
- Count devices:
-
Process Row 2 ("101"):
- Count devices:
cur = 2
(two '1's) - Since
cur > 0
:- Calculate beams:
pre * cur = 2 * 2 = 4
- Update total:
ans = 0 + 4 = 4
- Update previous:
pre = 2
- Calculate beams:
These 4 beams connect: device at (0,0) to both devices in row 2, and device at (0,2) to both devices in row 2
- Count devices:
-
Process Row 3 ("001"):
- Count devices:
cur = 1
(one '1') - Since
cur > 0
:- Calculate beams:
pre * cur = 2 * 1 = 2
- Update total:
ans = 4 + 2 = 6
- Update previous:
pre = 1
- Calculate beams:
These 2 beams connect the 2 devices in row 2 to the 1 device in row 3
- Count devices:
Final Result: Total beams = 6
The algorithm correctly identifies that beams form between:
- Row 0 and Row 2: 2 × 2 = 4 beams
- Row 2 and Row 3: 2 × 1 = 2 beams
- Total: 4 + 2 = 6 beams
Solution Implementation
1class Solution:
2 def numberOfBeams(self, bank: List[str]) -> int:
3 # Initialize total beam count and previous row's device count
4 total_beams = 0
5 prev_devices = 0
6
7 # Iterate through each row in the bank
8 for row in bank:
9 # Count the number of security devices (1s) in current row
10 curr_devices = row.count("1")
11
12 # If current row has devices
13 if curr_devices > 0:
14 # Add beams between previous row's devices and current row's devices
15 # Each device in previous row connects to each device in current row
16 total_beams += prev_devices * curr_devices
17
18 # Update previous row's device count for next iteration
19 prev_devices = curr_devices
20
21 return total_beams
22
1class Solution {
2 public int numberOfBeams(String[] bank) {
3 int totalBeams = 0; // Total number of laser beams
4 int previousDeviceCount = 0; // Number of devices in the previous non-empty row
5
6 // Iterate through each row in the bank
7 for (String row : bank) {
8 int currentDeviceCount = 0; // Count of security devices in current row
9
10 // Count the number of '1's (security devices) in the current row
11 for (int i = 0; i < row.length(); i++) {
12 currentDeviceCount += row.charAt(i) - '0';
13 }
14
15 // If current row has security devices
16 if (currentDeviceCount > 0) {
17 // Calculate beams between previous row and current row
18 // Each device in previous row connects to each device in current row
19 totalBeams += previousDeviceCount * currentDeviceCount;
20
21 // Update previous device count for next iteration
22 previousDeviceCount = currentDeviceCount;
23 }
24 }
25
26 return totalBeams;
27 }
28}
29
1class Solution {
2public:
3 int numberOfBeams(vector<string>& bank) {
4 int totalBeams = 0; // Total number of laser beams
5 int previousDeviceCount = 0; // Number of devices in the previous non-empty row
6
7 // Iterate through each row in the bank
8 for (const auto& row : bank) {
9 // Count the number of security devices ('1's) in the current row
10 int currentDeviceCount = count(row.begin(), row.end(), '1');
11
12 // If current row has devices, calculate beams with previous row
13 if (currentDeviceCount > 0) {
14 // Add beams formed between current row and previous non-empty row
15 // Each device in previous row connects to each device in current row
16 totalBeams += previousDeviceCount * currentDeviceCount;
17
18 // Update previous device count for next iteration
19 previousDeviceCount = currentDeviceCount;
20 }
21 }
22
23 return totalBeams;
24 }
25};
26
1/**
2 * Calculates the total number of laser beams between security devices in a bank.
3 * Each '1' in a row represents a security device that can form beams with devices in the previous row.
4 *
5 * @param bank - Array of strings representing the bank floor, where '1' is a device and '0' is empty space
6 * @returns The total number of laser beams between all security devices
7 */
8function numberOfBeams(bank: string[]): number {
9 // Initialize total beam count and device count from previous row with devices
10 let totalBeams: number = 0;
11 let previousRowDeviceCount: number = 0;
12
13 // Iterate through each row of the bank floor
14 for (const row of bank) {
15 // Count the number of '1's (devices) in the current row
16 // split('1').length - 1 gives the count of '1's in the string
17 const currentRowDeviceCount: number = row.split('1').length - 1;
18
19 // If current row has devices
20 if (currentRowDeviceCount > 0) {
21 // Each device in current row forms a beam with each device in previous row
22 totalBeams += previousRowDeviceCount * currentRowDeviceCount;
23
24 // Update previous row device count for next iteration
25 previousRowDeviceCount = currentRowDeviceCount;
26 }
27 // Skip rows with no devices (they don't block beams)
28 }
29
30 return totalBeams;
31}
32
Time and Space Complexity
Time Complexity: O(m × n)
, where m
is the number of rows in the bank array and n
is the length of each string (number of columns). The algorithm iterates through each row (m
iterations), and for each row, the count("1")
operation scans through all n
characters in the string, resulting in O(n)
time per row. Therefore, the total time complexity is O(m × n)
.
Space Complexity: O(1)
. The algorithm only uses a constant amount of extra space for variables ans
, pre
, and cur
, regardless of the input size. No additional data structures that scale with the input are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Counting Beams Within the Same Row
Issue: A common misunderstanding is thinking that devices within the same row can form laser beams with each other. This would lead to incorrectly adding combinations like C(n, 2)
for each row with n
devices.
Wrong Approach:
for row in bank: curr_devices = row.count("1") if curr_devices > 1: # WRONG: counting beams within the same row total_beams += curr_devices * (curr_devices - 1) // 2
Solution: Remember that laser beams only form between devices on different rows, never within the same row. The correct approach multiplies device counts between different rows only.
Pitfall 2: Incorrectly Handling the First Row
Issue: Some might think the first row with devices doesn't contribute any beams since there's no previous row, leading to skipping it entirely or handling it as a special case.
Wrong Approach:
first_row_processed = False for row in bank: curr_devices = row.count("1") if curr_devices > 0: if not first_row_processed: first_row_processed = True prev_devices = curr_devices continue # WRONG: skipping first row entirely total_beams += prev_devices * curr_devices prev_devices = curr_devices
Solution: The correct implementation naturally handles the first row. When prev_devices = 0
initially, the multiplication 0 * curr_devices = 0
correctly adds no beams for the first row, then updates prev_devices
for subsequent calculations.
Pitfall 3: Counting Beams Between Non-Adjacent Rows with Devices
Issue: Misinterpreting that devices can form beams across multiple empty rows might lead to tracking all previous rows with devices and calculating beams between all pairs.
Wrong Approach:
rows_with_devices = []
for i, row in enumerate(bank):
curr_devices = row.count("1")
if curr_devices > 0:
# WRONG: calculating beams between all pairs of rows
for prev_count in rows_with_devices:
total_beams += prev_count * curr_devices
rows_with_devices.append(curr_devices)
Solution: The problem clearly states beams only form between consecutive non-empty rows (ignoring empty rows in between). The correct approach only tracks the immediately previous non-empty row using a single variable prev_devices
.
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!