Facebook Pixel

3349. Adjacent Increasing Subarrays Detection I

Problem Description

You are given an array nums of n integers and an integer k. Your task is to determine whether there exist two adjacent subarrays of length k where both subarrays are strictly increasing.

The two subarrays must satisfy these conditions:

  • Each subarray has exactly k elements
  • Both subarrays are strictly increasing (each element is greater than the previous one)
  • The subarrays must be adjacent, meaning if the first subarray starts at index a, then the second subarray must start at index b = a + k (they don't overlap and are consecutive)

For example, if you have subarrays nums[a..a + k - 1] and nums[b..b + k - 1], they are adjacent when b = a + k.

Return true if you can find two such adjacent strictly increasing subarrays of length k, and false otherwise.

Example visualization: If nums = [1, 2, 3, 4, 5, 6] and k = 3:

  • First subarray: [1, 2, 3] (indices 0-2) - strictly increasing ✓
  • Second subarray: [4, 5, 6] (indices 3-5) - strictly increasing ✓
  • They are adjacent (second starts right after first ends) ✓
  • Result: true
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to find the longest strictly increasing sequences in the array and check if we can extract two adjacent subarrays of length k from them.

Think about it this way: if we have a long strictly increasing sequence, we might be able to split it into two parts of length k each. For instance, a strictly increasing sequence of length 6 can be split into two adjacent subarrays of length 3.

But there's another scenario: what if we have two separate strictly increasing sequences? If the first sequence ends right where the second begins, they form adjacent subarrays. For example, [1, 2, 3, 4, 2, 3, 4, 5] has two increasing sequences [1, 2, 3, 4] and [2, 3, 4, 5] that meet at indices 3 and 4.

This leads us to track:

  1. The length of the current strictly increasing sequence (cur)
  2. The length of the previous strictly increasing sequence (pre)

For each position, we check if the sequence continues to increase. When it breaks (or we reach the end):

  • We can either split the current sequence in half to get two adjacent subarrays (hence cur // 2)
  • Or we can use the end of the previous sequence and the beginning of the current sequence (hence min(pre, cur))

The maximum of these possibilities (mx) tells us the longest possible length for two adjacent strictly increasing subarrays. If mx >= k, we know we can find two adjacent subarrays of length k.

The condition x >= nums[i + 1] detects when the strictly increasing property breaks, signaling the end of a sequence and the potential start of a new one.

Solution Approach

The solution uses a single-pass algorithm with three variables to track increasing sequences:

  1. cur: Tracks the length of the current strictly increasing sequence
  2. pre: Stores the length of the previous strictly increasing sequence
  3. mx: Maintains the maximum possible length of two adjacent strictly increasing subarrays found so far

Algorithm walkthrough:

  1. Initialize variables: Start with mx = pre = cur = 0

  2. Iterate through the array: For each element x at index i:

    • Increment cur by 1 (extending the current sequence)
  3. Check for sequence break: The sequence breaks when:

    • We reach the last element (i == len(nums) - 1), OR
    • The current element is greater than or equal to the next element (x >= nums[i + 1])
  4. When a sequence ends, calculate the maximum possible length of two adjacent subarrays:

    • Option 1: Split the current sequence in half → cur // 2
      • Example: A sequence of length 6 can give us two adjacent subarrays of length 3
    • Option 2: Use the tail of the previous sequence and the head of the current sequence → min(pre, cur)
      • Example: If pre = 4 and cur = 3, we can use the last 3 elements of pre and all 3 elements of cur
    • Update mx with the maximum of these options: mx = max(mx, cur // 2, min(pre, cur))
  5. Prepare for the next sequence:

    • Set pre = cur (current becomes previous)
    • Reset cur = 0 (start counting the new sequence)
  6. Return the result: Check if mx >= k

    • If true, we can find two adjacent strictly increasing subarrays of length k
    • If false, no such pair exists

Time Complexity: O(n) - single pass through the array
Space Complexity: O(1) - only using a few variables

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with nums = [2, 5, 7, 8, 9, 2, 3, 4, 3, 1] and k = 3.

We need to find if there exist two adjacent strictly increasing subarrays of length 3.

Initial state: mx = 0, pre = 0, cur = 0

Step-by-step iteration:

inums[i]cur (before)Actioncur (after)premxExplanation
020cur++100Start of array, building sequence
151cur++2002 < 5, sequence continues
272cur++3005 < 7, sequence continues
383cur++4007 < 8, sequence continues
494cur++5008 < 9, sequence continues
525Break!9 ≥ 2, sequence breaks

At break (i=5):

  • Current sequence [2,5,7,8,9] has length cur = 5
  • Calculate: max(0, 5//2, min(0,5)) = max(0, 2, 0) = 2
  • Update: mx = 2, pre = 5, cur = 0

Continue iteration:

inums[i]cur (before)Actioncur (after)premxExplanation
520cur++152New sequence starts
631cur++2522 < 3, sequence continues
742cur++3523 < 4, sequence continues
833Break!4 ≥ 3, sequence breaks

At break (i=8):

  • Current sequence [2,3,4] has length cur = 3
  • Calculate: max(2, 3//2, min(5,3)) = max(2, 1, 3) = 3
  • Update: mx = 3, pre = 3, cur = 0

Continue to end:

inums[i]cur (before)Actioncur (after)premxExplanation
830cur++133New sequence starts
911End reachedLast element

At end (i=9):

  • Current sequence [3,1] has length cur = 1
  • Calculate: max(3, 1//2, min(3,1)) = max(3, 0, 1) = 3
  • Final: mx = 3

Result: Since mx = 3 >= k = 3, return true.

The algorithm found that we can have two adjacent strictly increasing subarrays of length 3. Looking back at our array, one valid pair would be:

  • Subarray 1: [5, 7, 8] at indices 1-3
  • Subarray 2: [2, 3, 4] at indices 5-7

These aren't adjacent in the original array, but the algorithm correctly identifies that the maximum possible length is 3, which comes from the sequence [2, 5, 7, 8, 9] where we could take [5, 7, 8] and the sequence [2, 3, 4] that follows the break.

Solution Implementation

1class Solution:
2    def hasIncreasingSubarrays(self, nums: List[int], k: int) -> bool:
3        """
4        Check if there exist two adjacent strictly increasing subarrays of length k.
5      
6        Args:
7            nums: Input array of integers
8            k: Required length of each subarray
9          
10        Returns:
11            True if two adjacent strictly increasing subarrays of length k exist
12        """
13        # Track the maximum achievable length of valid adjacent subarrays
14        max_valid_length = 0
15      
16        # Length of the previous strictly increasing subarray
17        previous_length = 0
18      
19        # Length of the current strictly increasing subarray being built
20        current_length = 0
21      
22        # Iterate through the array with index and value
23        for i, current_value in enumerate(nums):
24            # Extend current strictly increasing subarray
25            current_length += 1
26          
27            # Check if we've reached the end of a strictly increasing sequence
28            # This happens when we're at the last element or next element is not greater
29            if i == len(nums) - 1 or current_value >= nums[i + 1]:
30                # Update maximum valid length considering two cases:
31                # 1. Split current subarray into two equal parts (current_length // 2)
32                # 2. Use previous and current as two adjacent subarrays (min of their lengths)
33                max_valid_length = max(
34                    max_valid_length,
35                    current_length // 2,
36                    min(previous_length, current_length)
37                )
38              
39                # Move to next sequence: current becomes previous, reset current
40                previous_length = current_length
41                current_length = 0
42      
43        # Check if we can form two adjacent subarrays of at least length k
44        return max_valid_length >= k
45
1class Solution {
2    public boolean hasIncreasingSubarrays(List<Integer> nums, int k) {
3        // Track the maximum valid length found
4        int maxValidLength = 0;
5        // Length of the previous strictly increasing subarray
6        int previousLength = 0;
7        // Length of the current strictly increasing subarray being processed
8        int currentLength = 0;
9      
10        int n = nums.size();
11      
12        // Iterate through all elements in the list
13        for (int i = 0; i < n; i++) {
14            // Increment current subarray length
15            currentLength++;
16          
17            // Check if we've reached the end of a strictly increasing subarray
18            // This happens when we're at the last element OR the next element is not greater
19            if (i == n - 1 || nums.get(i) >= nums.get(i + 1)) {
20                // Update maximum valid length considering two cases:
21                // 1. Using half of the current subarray (split into two equal parts)
22                // 2. Using adjacent subarrays (minimum of previous and current lengths)
23                maxValidLength = Math.max(maxValidLength, 
24                                         Math.max(currentLength / 2, 
25                                                 Math.min(previousLength, currentLength)));
26              
27                // Current subarray becomes the previous for next iteration
28                previousLength = currentLength;
29                // Reset current length for the next subarray
30                currentLength = 0;
31            }
32        }
33      
34        // Check if we found any valid configuration with length at least k
35        return maxValidLength >= k;
36    }
37}
38
1class Solution {
2public:
3    bool hasIncreasingSubarrays(vector<int>& nums, int k) {
4        int maxValidLength = 0;      // Maximum length of valid adjacent subarrays found
5        int previousLength = 0;       // Length of the previous strictly increasing segment
6        int currentLength = 0;        // Length of the current strictly increasing segment
7        int n = nums.size();
8      
9        for (int i = 0; i < n; ++i) {
10            // Increment the current segment length
11            ++currentLength;
12          
13            // Check if we've reached the end of a strictly increasing segment
14            // This happens when we're at the last element OR the next element is not greater
15            if (i == n - 1 || nums[i] >= nums[i + 1]) {
16                // Update the maximum valid length considering three cases:
17                // 1. Keep the current maximum
18                // 2. Split the current segment into two equal parts (currentLength / 2)
19                // 3. Use the minimum of previous and current segment lengths as adjacent pairs
20                maxValidLength = max({maxValidLength, 
21                                     currentLength / 2, 
22                                     min(previousLength, currentLength)});
23              
24                // Move to the next segment
25                previousLength = currentLength;
26                currentLength = 0;
27            }
28        }
29      
30        // Check if we found adjacent subarrays of at least length k
31        return maxValidLength >= k;
32    }
33};
34
1/**
2 * Checks if there exist two adjacent strictly increasing subarrays of length k
3 * @param nums - The input array of numbers
4 * @param k - The required length of each subarray
5 * @returns true if two adjacent strictly increasing subarrays of length k exist, false otherwise
6 */
7function hasIncreasingSubarrays(nums: number[], k: number): boolean {
8    // Track the maximum valid k value found, previous segment length, and current segment length
9    let maxValidK: number = 0;
10    let previousSegmentLength: number = 0;
11    let currentSegmentLength: number = 0;
12  
13    const arrayLength: number = nums.length;
14  
15    // Iterate through the array to find strictly increasing segments
16    for (let i = 0; i < arrayLength; ++i) {
17        // Increment current segment length
18        ++currentSegmentLength;
19      
20        // Check if we've reached the end of array or found a non-increasing point
21        if (i === arrayLength - 1 || nums[i] >= nums[i + 1]) {
22            // Update maximum valid k value considering:
23            // 1. Half of current segment (for two adjacent subarrays within same segment)
24            // 2. Minimum of previous and current segment (for adjacent subarrays across segments)
25            maxValidK = Math.max(
26                maxValidK, 
27                Math.floor(currentSegmentLength / 2), 
28                Math.min(previousSegmentLength, currentSegmentLength)
29            );
30          
31            // Move current segment to previous, reset current segment counter
32            previousSegmentLength = currentSegmentLength;
33            currentSegmentLength = 0;
34        }
35    }
36  
37    // Return true if we found a valid configuration with length >= k
38    return maxValidK >= k;
39}
40

Time and Space Complexity

Time Complexity: O(n) where n is the length of the input array nums. The algorithm iterates through the array exactly once using a single for loop. Each operation inside the loop (comparisons, arithmetic operations, and variable assignments) takes constant time O(1). Therefore, the overall time complexity is linear.

Space Complexity: O(1). The algorithm uses only a fixed number of variables (mx, pre, cur, i, and x) regardless of the input size. No additional data structures that scale with the input are created. The space usage remains constant throughout the execution.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Misunderstanding "Adjacent" Subarrays

The Problem: Many developers initially interpret "adjacent" as overlapping or sharing elements. For instance, they might think subarrays [1,2,3] and [2,3,4] are adjacent because they share elements 2 and 3.

Why It Happens: The term "adjacent" in everyday language often means "next to each other" which could imply sharing boundaries.

The Correct Understanding: Adjacent means the two subarrays are consecutive without overlap. If the first subarray ends at index i, the second must start at index i+1.

Solution:

# WRONG: Checking overlapping subarrays
for i in range(len(nums) - k + 1):
    first = nums[i:i+k]
    second = nums[i+1:i+k+1]  # This overlaps!
  
# CORRECT: Non-overlapping adjacent subarrays
for i in range(len(nums) - 2*k + 1):
    first = nums[i:i+k]
    second = nums[i+k:i+2*k]  # Starts right after first ends

Pitfall 2: Incorrectly Calculating Maximum Valid Length

The Problem: Forgetting to consider that a single long increasing sequence can be split into two adjacent subarrays.

Why It Happens: The intuitive approach focuses on finding two separate increasing sequences and checking if they're adjacent, missing the case where one sequence contains both subarrays.

Example: With nums = [1,2,3,4,5,6] and k = 3, there's one increasing sequence of length 6 that can be split into [1,2,3] and [4,5,6].

Solution:

# Always consider both options when a sequence ends:
max_valid_length = max(
    max_valid_length,
    current_length // 2,  # Split single sequence
    min(previous_length, current_length)  # Use two sequences
)

Pitfall 3: Off-by-One Error in Sequence Break Detection

The Problem: Incorrectly determining when a strictly increasing sequence ends, particularly forgetting the "strictly" part (where equal values also break the sequence).

Why It Happens: Developers might check only nums[i] > nums[i+1] instead of nums[i] >= nums[i+1].

Example: For [1,2,2,3,4,5] with k = 2, the sequence breaks at index 1 (where 2 >= 2), not continuing through.

Solution:

# WRONG: Only checking for decrease
if i == len(nums) - 1 or current_value > nums[i + 1]:
  
# CORRECT: Checking for non-increase (includes equal values)
if i == len(nums) - 1 or current_value >= nums[i + 1]:

Pitfall 4: Not Handling Edge Cases

The Problem: Failing to handle arrays where k is larger than half the array length or when k = 1.

Why It Happens: The algorithm logic might seem to handle all cases, but special cases need verification.

Examples:

  • k = 1: Any two consecutive elements form valid adjacent subarrays (since single elements are trivially "strictly increasing")
  • k > len(nums) // 2: Impossible to have two non-overlapping subarrays

Solution:

# Early return for edge cases
if k == 1:
    return True  # Any two elements work
if k > len(nums) // 2:
    return False  # Not enough space for two subarrays
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

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

Load More