Facebook Pixel

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 and 5 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).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. sensor1 dropped its value at position i, so sensor1[i] now contains what was originally at position i+1
  2. sensor2 dropped its value at position i, so sensor2[i] now contains what was originally at position i+1

To determine which sensor is defective, we can check the alignment pattern after position i:

  • If sensor1[i+1] == sensor2[i], then sensor1 is shifted (meaning it dropped a value)
  • If sensor2[i+1] == sensor1[i], then sensor2 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 means sensor1 cannot be the shifted version of the correct data, so sensor1 is defective (return 1)
  • If sensor1[i] != sensor2[i + 1], it means sensor2 cannot be the shifted version of the correct data, so sensor2 is defective (return 2)
  • 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 Evaluator

Example 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: Does sensor1[i+1] match sensor2[i]?
    • sensor1[3] = 7 vs sensor2[2] = 4 β†’ No match
  • Check if sensor2 is defective: Does sensor2[i+1] match sensor1[i]?
    • sensor2[3] = 5 vs sensor1[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 beyond
  • sensor2 dropped value at position 2, causing everything to shift left
  • Original data in sensor1: [2, 3, 5, 7]
  • sensor2 dropped the 5 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] vs sensor2[i]: sensor1[3] = 5 vs sensor2[2] = 3 β†’ No match
  • Check sensor2[i+1] vs sensor1[i]: sensor2[3] = 4 vs sensor1[2] = 4 β†’ Match!

Move to position 3:

  • Check sensor1[i+1] vs sensor2[i]: sensor1[4] = 9 vs sensor2[3] = 4 β†’ No match
  • Check sensor2[i+1] vs sensor1[i]: sensor2[4] = 5 vs sensor1[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 takes O(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 most O(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 and n 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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More