1826. Faulty Sensor π
Problem Description
You are given two sensor arrays sensor1
and sensor2
that should contain identical data collected from an experiment. However, one of the sensors might be defective.
When a sensor is defective, it drops exactly one data point. After dropping a data point:
- All data points to the right shift one position to the left
- The last position gets filled with a random value (guaranteed to be different from the dropped value)
For example, if the correct data is [1,2,3,4,5]
and the value 3
is dropped, the defective sensor might return [1,2,4,5,7]
where:
- Values
4
and5
shifted left - Position 5 is filled with random value
7
Given the two sensor arrays:
- Return
1
if sensor1 is defective - Return
2
if sensor2 is defective - Return
-1
if you cannot determine which sensor is defective or if neither is defective
The key insight is that at most one sensor can be defective. By comparing the arrays element by element, you can identify where they first differ. From that point, you can check if one array matches the other when accounting for the shifted pattern that would result from a dropped value.
The solution finds the first position where the arrays differ, then checks if sensor1[i+1]
matches sensor2[i]
(indicating sensor1 dropped a value at position i
) or if sensor2[i+1]
matches sensor1[i]
(indicating sensor2 dropped a value at position i
).
Intuition
When two sensors should have identical data but one might have dropped a value, the arrays will be identical up to the point where the drop occurred. This is our starting observation.
Let's think about what happens when a value is dropped. If we have original data [a, b, c, d, e]
and value c
is dropped, we get [a, b, d, e, x]
where x
is some random value. Notice that:
- Elements before the dropped position remain unchanged
- Elements after the dropped position shift left by one
- The last element becomes unpredictable
Now, when comparing two arrays where one might have a dropped value, we'll see matching elements until we reach the drop point. At position i
where they first differ, one of two things happened:
sensor1
dropped its value at positioni
, sosensor1[i]
now contains what was originally at positioni+1
sensor2
dropped its value at positioni
, sosensor2[i]
now contains what was originally at positioni+1
To determine which sensor is defective, we can check the alignment pattern after position i
:
- If
sensor1[i+1] == sensor2[i]
, thensensor1
is shifted (meaning it dropped a value) - If
sensor2[i+1] == sensor1[i]
, thensensor2
is shifted (meaning it dropped a value)
The beauty of this approach is that we only need to check these alignment patterns starting from where the arrays first differ. If both patterns hold true simultaneously throughout the remaining elements (except possibly the last one), then we cannot determine which sensor is defective, returning -1
.
Learn more about Two Pointers patterns.
Solution Approach
The implementation follows a two-phase traversal approach:
Phase 1: Find the first difference
i, n = 0, len(sensor1)
while i < n - 1:
if sensor1[i] != sensor2[i]:
break
i += 1
We iterate through both arrays simultaneously until we find the first position i
where sensor1[i] != sensor2[i]
. We stop at n-1
because if we reach the last element without finding a difference, the arrays might differ only at the last position (which could be the random replacement value).
Phase 2: Determine which sensor is defective
while i < n - 1: if sensor1[i + 1] != sensor2[i]: return 1 if sensor1[i] != sensor2[i + 1]: return 2 i += 1
Starting from the first difference position, we check the alignment patterns:
- If
sensor1[i + 1] != sensor2[i]
, it meanssensor1
cannot be the shifted version of the correct data, sosensor1
is defective (return1
) - If
sensor1[i] != sensor2[i + 1]
, it meanssensor2
cannot be the shifted version of the correct data, sosensor2
is defective (return2
) - If both conditions are false (meaning both alignments match), we continue checking the next position
The loop continues until i < n - 1
because we need to compare sensor1[i+1]
and sensor2[i+1]
, which requires staying within array bounds.
Return -1 for undetermined cases If we complete the second loop without returning, it means:
- Either both sensors show consistent shifted patterns (impossible to determine which is defective)
- Or the arrays only differ at the last position (could be the random value in either sensor)
- Or the arrays are completely identical (no defect)
In all these cases, we return -1
to indicate the defective sensor cannot be determined.
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 an example where sensor1 = [2,3,5,7]
and sensor2 = [2,3,4,5]
.
Step 1: Find the first difference
- Compare position 0:
sensor1[0] = 2
,sensor2[0] = 2
β Match, continue - Compare position 1:
sensor1[1] = 3
,sensor2[1] = 3
β Match, continue - Compare position 2:
sensor1[2] = 5
,sensor2[2] = 4
β First difference found!
We stop at position i = 2
where the arrays first differ.
Step 2: Check alignment patterns
Starting from position i = 2
:
- Check if
sensor1
is defective: Doessensor1[i+1]
matchsensor2[i]
?sensor1[3] = 7
vssensor2[2] = 4
β No match
- Check if
sensor2
is defective: Doessensor2[i+1]
matchsensor1[i]
?sensor2[3] = 5
vssensor1[2] = 5
β Match!
Since sensor2[3] = sensor1[2]
, this indicates that sensor2
has a shifted pattern. This means sensor2
dropped a value at position 2.
Verification of the pattern:
- Original correct data was likely:
[2, 3, 4, 5]
sensor1
kept all values:[2, 3, 5, 7]
(wait, this doesn't match...)
Let me reconsider. Actually, if sensor2[3] = sensor1[2]
, it means:
sensor1
has the correct sequence at positions 2 and beyondsensor2
dropped value at position 2, causing everything to shift left- Original data in
sensor1
:[2, 3, 5, 7]
sensor2
dropped the5
at position 2:[2, 3, ~~5~~, 7]
β[2, 3, 7, ?]
β[2, 3, 4, 5]
Actually, let me use a clearer example:
Let's walk through where sensor1 = [1,2,4,5,9]
and sensor2 = [1,2,3,4,5]
.
Step 1: Find the first difference
- Position 0:
sensor1[0] = 1
,sensor2[0] = 1
β Match - Position 1:
sensor1[1] = 2
,sensor2[1] = 2
β Match - Position 2:
sensor1[2] = 4
,sensor2[2] = 3
β First difference at i = 2
Step 2: Check alignment patterns from position 2
- Check
sensor1[i+1]
vssensor2[i]
:sensor1[3] = 5
vssensor2[2] = 3
β No match - Check
sensor2[i+1]
vssensor1[i]
:sensor2[3] = 4
vssensor1[2] = 4
β Match!
Move to position 3:
- Check
sensor1[i+1]
vssensor2[i]
:sensor1[4] = 9
vssensor2[3] = 4
β No match - Check
sensor2[i+1]
vssensor1[i]
:sensor2[4] = 5
vssensor1[3] = 5
β Match!
The pattern sensor2[i+1] = sensor1[i]
consistently holds, meaning sensor2
is shifted relative to sensor1
. This indicates sensor1 dropped a value at position 2.
Understanding why: The original data was [1,2,3,4,5]
. Sensor1 dropped the 3
, resulting in [1,2,4,5,9]
where 9
is the random fill value. Sensor2 correctly shows [1,2,3,4,5]
.
Result: Return 1
(sensor1 is defective)
Solution Implementation
1class Solution:
2 def badSensor(self, sensor1: List[int], sensor2: List[int]) -> int:
3 """
4 Determines which sensor is defective when one sensor drops exactly one reading.
5
6 Args:
7 sensor1: List of readings from first sensor
8 sensor2: List of readings from second sensor
9
10 Returns:
11 1 if sensor1 is defective, 2 if sensor2 is defective,
12 -1 if cannot determine which sensor is defective
13 """
14 # Find the first position where sensors differ
15 index = 0
16 n = len(sensor1)
17
18 # Skip all matching readings at the beginning
19 while index < n - 1:
20 if sensor1[index] != sensor2[index]:
21 break
22 index += 1
23
24 # Check which sensor dropped a reading by comparing shifted sequences
25 while index < n - 1:
26 # Check if sensor1 dropped a reading at position 'index'
27 # (sensor1[index+1:] should match sensor2[index:n-1])
28 if sensor1[index + 1] != sensor2[index]:
29 return 1
30
31 # Check if sensor2 dropped a reading at position 'index'
32 # (sensor2[index+1:] should match sensor1[index:n-1])
33 if sensor1[index] != sensor2[index + 1]:
34 return 2
35
36 index += 1
37
38 # Cannot determine which sensor is defective
39 return -1
40
1class Solution {
2 public int badSensor(int[] sensor1, int[] sensor2) {
3 int n = sensor1.length;
4
5 // Find the first position where sensors differ
6 int firstDifferenceIndex = 0;
7 while (firstDifferenceIndex < n - 1 && sensor1[firstDifferenceIndex] == sensor2[firstDifferenceIndex]) {
8 firstDifferenceIndex++;
9 }
10
11 // Check which sensor dropped a value by comparing shifted sequences
12 for (int i = firstDifferenceIndex; i < n - 1; i++) {
13 // Check if sensor1 dropped a value (sensor2 is correct)
14 // Compare sensor1[i+1] with sensor2[i] (sensor1 shifted left)
15 if (sensor1[i + 1] != sensor2[i]) {
16 return 1; // sensor1 is defective
17 }
18
19 // Check if sensor2 dropped a value (sensor1 is correct)
20 // Compare sensor1[i] with sensor2[i+1] (sensor2 shifted left)
21 if (sensor1[i] != sensor2[i + 1]) {
22 return 2; // sensor2 is defective
23 }
24 }
25
26 // Cannot determine which sensor is defective
27 return -1;
28 }
29}
30
1class Solution {
2public:
3 int badSensor(vector<int>& sensor1, vector<int>& sensor2) {
4 int n = sensor1.size();
5
6 // Find the first position where the two sensors differ
7 int firstDifferenceIndex = 0;
8 while (firstDifferenceIndex < n - 1 &&
9 sensor1[firstDifferenceIndex] == sensor2[firstDifferenceIndex]) {
10 firstDifferenceIndex++;
11 }
12
13 // Check if either sensor could be defective by comparing shifted sequences
14 // If sensor1 is defective (dropped a value), then sensor1[i+1] should match sensor2[i]
15 // If sensor2 is defective (dropped a value), then sensor2[i+1] should match sensor1[i]
16 for (int i = firstDifferenceIndex; i < n - 1; i++) {
17 // If sensor1's next element doesn't match sensor2's current element,
18 // sensor1 cannot be the defective one
19 if (sensor1[i + 1] != sensor2[i]) {
20 return 1;
21 }
22
23 // If sensor2's next element doesn't match sensor1's current element,
24 // sensor2 cannot be the defective one
25 if (sensor1[i] != sensor2[i + 1]) {
26 return 2;
27 }
28 }
29
30 // Cannot determine which sensor is defective
31 return -1;
32 }
33};
34
1/**
2 * Determines which sensor is defective by comparing their readings
3 * @param sensor1 - Array of readings from the first sensor
4 * @param sensor2 - Array of readings from the second sensor
5 * @returns 1 if sensor1 is defective, 2 if sensor2 is defective, -1 if cannot determine
6 */
7function badSensor(sensor1: number[], sensor2: number[]): number {
8 // Initialize index for traversing both sensor arrays
9 let currentIndex: number = 0;
10 const arrayLength: number = sensor1.length;
11
12 // Find the first position where sensors disagree
13 while (currentIndex < arrayLength - 1) {
14 if (sensor1[currentIndex] !== sensor2[currentIndex]) {
15 break;
16 }
17 currentIndex++;
18 }
19
20 // Check which sensor has a defective reading by comparing shifted values
21 while (currentIndex < arrayLength - 1) {
22 // Check if sensor1 is defective (sensor1 dropped a value)
23 // Compare sensor1[i+1] with sensor2[i] to see if sensor1 skipped a reading
24 if (sensor1[currentIndex + 1] !== sensor2[currentIndex]) {
25 return 1;
26 }
27
28 // Check if sensor2 is defective (sensor2 dropped a value)
29 // Compare sensor1[i] with sensor2[i+1] to see if sensor2 skipped a reading
30 if (sensor1[currentIndex] !== sensor2[currentIndex + 1]) {
31 return 2;
32 }
33
34 currentIndex++;
35 }
36
37 // Cannot determine which sensor is defective
38 return -1;
39}
40
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the sensor arrays.
The algorithm consists of two sequential while loops:
- The first loop iterates through the arrays to find the first position where
sensor1[i] != sensor2[i]
. In the worst case, this takesO(n-1)
iterations. - The second loop continues from position
i
and iterates through the remaining elements, performing constant-time comparisons at each step. This also takes at mostO(n-1-i)
iterations.
Since both loops are sequential and not nested, the total time complexity is O(n-1) + O(n-1-i) = O(n)
.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- Two integer variables
i
andn
to track the current index and array length - No additional data structures are created
The space usage remains constant regardless of the input size, resulting in O(1)
space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Boundary Handling in the First Loop
A common mistake is using while i < n
instead of while i < n - 1
in the first loop. This can cause an index out of bounds error when accessing sensor1[i+1]
or sensor2[i+1]
in the second loop.
Incorrect:
# This will cause index error if arrays differ at last position while i < n: if sensor1[i] != sensor2[i]: break i += 1
Correct:
while i < n - 1: if sensor1[i] != sensor2[i]: break i += 1
2. Misunderstanding the Shift Pattern
Many solutions incorrectly assume they need to compare the entire remaining subarrays after finding the first difference. The key insight is that you only need to check element-by-element alignment, not slice comparison.
Incorrect approach:
# Comparing entire subarrays is inefficient and unnecessary if sensor1[i+1:] == sensor2[i:n-1]: return 1 if sensor2[i+1:] == sensor1[i:n-1]: return 2
Correct approach:
# Check element-by-element as we iterate while index < n - 1: if sensor1[index + 1] != sensor2[index]: return 1 if sensor1[index] != sensor2[index + 1]: return 2 index += 1
3. Forgetting Edge Cases Where Arrays Are Identical
If both arrays are completely identical, the first loop will reach n-1
without finding any difference. The solution should return -1
in this case since there's no defective sensor.
Missing edge case handling:
# Assuming there's always a difference i = 0 while sensor1[i] == sensor2[i]: # This could go out of bounds i += 1
Proper handling:
# The loop bounds ensure we don't go out of range while i < n - 1: if sensor1[i] != sensor2[i]: break i += 1 # If i == n-1, arrays might be identical or differ only at last position
4. Early Return Without Complete Verification
Some implementations return immediately after finding the first matching shifted element, without verifying the entire remaining sequence maintains the shift pattern.
Incorrect:
# Returns too early without verifying the entire pattern if i < n - 1: if sensor1[i + 1] == sensor2[i]: return 1 # Wrong! Need to verify entire shifted sequence if sensor2[i + 1] == sensor1[i]: return 2 # Wrong! Need to verify entire shifted sequence
Correct:
# Continue checking until we find a mismatch in the pattern while index < n - 1: if sensor1[index + 1] != sensor2[index]: return 1 # Pattern broken, sensor1 is defective if sensor1[index] != sensor2[index + 1]: return 2 # Pattern broken, sensor2 is defective index += 1
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
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!