Facebook Pixel

413. Arithmetic Slices

Problem Description

You need to find how many arithmetic subarrays exist in a given integer array.

An arithmetic array must have:

  • At least 3 elements
  • The same difference between any two consecutive elements

For example:

  • [1,3,5,7,9] is arithmetic (difference = 2)
  • [7,7,7,7] is arithmetic (difference = 0)
  • [3,-1,-5,-9] is arithmetic (difference = -4)

A subarray is a contiguous portion of the original array. You need to count all possible subarrays that form arithmetic sequences.

Given an array nums, return the total count of arithmetic subarrays.

Example breakdown: If nums = [1,2,3,4], the arithmetic subarrays are:

  • [1,2,3] (difference = 1)
  • [2,3,4] (difference = 1)
  • [1,2,3,4] (difference = 1)

So the answer would be 3.

The key insight is that when you have a longer arithmetic sequence, it contains multiple smaller arithmetic subsequences. For instance, an arithmetic sequence of length n contains (n-1)(n-2)/2 arithmetic subarrays of length 3 or more.

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

Intuition

The key observation is that when we extend an arithmetic sequence by one more element, we create additional arithmetic subarrays.

Consider an arithmetic sequence like [1,2,3,4,5]. When we're at element 5, we know it extends the existing arithmetic pattern. This creates new subarrays:

  • [3,4,5] (length 3)
  • [2,3,4,5] (length 4)
  • [1,2,3,4,5] (length 5)

Notice that if we already have an arithmetic sequence of length k, adding one more element that maintains the same difference creates k-1 new arithmetic subarrays (all ending at the new element).

This leads us to a counting strategy: as we scan through the array, we track how many consecutive elements maintain the same difference. Each time we extend the sequence, we add the count of new subarrays formed.

For example, with [1,2,3,4]:

  • At position 2 (value 3): We have our first arithmetic subarray [1,2,3], count = 1
  • At position 3 (value 4): We extend the sequence, creating [2,3,4] and [1,2,3,4], adding 2 more

The pattern emerges: if cnt represents how many times we've extended the current arithmetic sequence, then adding one more element creates cnt new arithmetic subarrays. This is because each extension adds subarrays of all possible lengths ending at the current position.

When the difference changes, we reset our count since we're starting a new potential arithmetic sequence. The elegance of this approach is that we only need to track the current difference and how many extensions we've made, giving us an O(n) solution with O(1) space.

Solution Approach

The implementation uses a sliding window-like approach with two key variables:

  • d: tracks the current difference between consecutive elements (initialized to 3000, an impossible difference given the problem constraints)
  • cnt: counts how many times we've extended the current arithmetic sequence (initialized to 0)

Algorithm walkthrough:

  1. Iterate through adjacent pairs: Using pairwise(nums), we examine each pair of consecutive elements (a, b).

  2. Check if the sequence continues: For each pair, calculate the difference b - a:

    • If b - a == d, the arithmetic sequence continues:
      • Increment cnt by 1
      • Add cnt to the answer (this represents all new arithmetic subarrays ending at position b)
    • If b - a != d, we've encountered a new difference:
      • Update d = b - a to track the new difference
      • Reset cnt = 0 since we're starting a new potential sequence
  3. Accumulate the count: The key insight is that when cnt = k, it means we've found k new arithmetic subarrays ending at the current position:

    • When cnt = 1: one subarray of length 3
    • When cnt = 2: two subarrays (lengths 3 and 4)
    • When cnt = k: k subarrays (lengths 3 through k+2)

Example trace with nums = [1,2,3,4]:

  • Pair (1,2): difference = 1, new difference, set d=1, cnt=0, ans=0
  • Pair (2,3): difference = 1 = d, increment cnt=1, add to ans=0+1=1
  • Pair (3,4): difference = 1 = d, increment cnt=2, add to ans=1+2=3

The algorithm correctly counts 3 arithmetic subarrays: [1,2,3], [2,3,4], and [1,2,3,4].

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

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 trace through the algorithm with nums = [1, 3, 5, 6, 7, 8]:

Initial state: d = 3000 (impossible difference), cnt = 0, ans = 0

Step 1: Pair (1, 3)

  • Difference: 3 - 1 = 2
  • Since 2 ≠ 3000, this is a new difference
  • Update: d = 2, cnt = 0, ans = 0

Step 2: Pair (3, 5)

  • Difference: 5 - 3 = 2
  • Since 2 == d, the arithmetic sequence continues
  • Update: cnt = 1, ans = 0 + 1 = 1
  • This counts the subarray [1, 3, 5]

Step 3: Pair (5, 6)

  • Difference: 6 - 5 = 1
  • Since 1 ≠ 2, we have a new difference
  • Update: d = 1, cnt = 0, ans = 1

Step 4: Pair (6, 7)

  • Difference: 7 - 6 = 1
  • Since 1 == d, the arithmetic sequence continues
  • Update: cnt = 1, ans = 1 + 1 = 2
  • This counts the subarray [5, 6, 7]

Step 5: Pair (7, 8)

  • Difference: 8 - 7 = 1
  • Since 1 == d, the arithmetic sequence continues
  • Update: cnt = 2, ans = 2 + 2 = 4
  • This counts two new subarrays: [6, 7, 8] and [5, 6, 7, 8]

Final answer: 4 arithmetic subarrays

  • [1, 3, 5] (difference = 2)
  • [5, 6, 7] (difference = 1)
  • [6, 7, 8] (difference = 1)
  • [5, 6, 7, 8] (difference = 1)

The algorithm efficiently counts all arithmetic subarrays by tracking when consecutive differences remain the same, accumulating the count as sequences extend.

Solution Implementation

1from typing import List
2from itertools import pairwise
3
4class Solution:
5    def numberOfArithmeticSlices(self, nums: List[int]) -> int:
6        # Total count of arithmetic slices
7        total_slices = 0
8      
9        # Count of consecutive pairs with the same difference
10        # (which forms arithmetic slices ending at current position)
11        consecutive_pairs = 0
12      
13        # Initialize with an impossible difference value
14        # (since array values are in range [-1000, 1000], diff is at most 2000)
15        current_difference = 3000
16      
17        # Iterate through consecutive pairs in the array
18        for prev_num, curr_num in pairwise(nums):
19            difference = curr_num - prev_num
20          
21            if difference == current_difference:
22                # Same difference as before, extend the arithmetic sequence
23                consecutive_pairs += 1
24            else:
25                # Different difference, start a new potential arithmetic sequence
26                current_difference = difference
27                consecutive_pairs = 0
28          
29            # Add the count of arithmetic slices ending at current position
30            # If we have k consecutive pairs with same difference,
31            # we can form k arithmetic slices ending here
32            total_slices += consecutive_pairs
33      
34        return total_slices
35
1class Solution {
2    public int numberOfArithmeticSlices(int[] nums) {
3        // Total count of arithmetic slices
4        int totalCount = 0;
5      
6        // Count of consecutive pairs with the same difference
7        // This represents how many arithmetic slices end at current position
8        int currentStreakCount = 0;
9      
10        // Initialize with an impossible difference value
11        // (since array values are in range [-1000, 1000], max difference is 2000)
12        int currentDifference = 3000;
13      
14        // Iterate through consecutive pairs in the array
15        for (int i = 0; i < nums.length - 1; i++) {
16            int difference = nums[i + 1] - nums[i];
17          
18            if (difference == currentDifference) {
19                // Same difference as previous pair, extend the streak
20                currentStreakCount++;
21            } else {
22                // Different difference, start a new streak
23                currentDifference = difference;
24                currentStreakCount = 0;
25            }
26          
27            // Add current streak count to total
28            // When we have k consecutive pairs with same difference,
29            // we can form k arithmetic slices ending at current position
30            totalCount += currentStreakCount;
31        }
32      
33        return totalCount;
34    }
35}
36
1class Solution {
2public:
3    int numberOfArithmeticSlices(vector<int>& nums) {
4        int totalCount = 0;      // Total number of arithmetic slices found
5        int currentLength = 0;    // Length of current arithmetic sequence (minus 2)
6        int prevDifference = 3000; // Previous difference between consecutive elements
7                                  // Initialized to an impossible value (outside [-1000, 1000])
8      
9        // Iterate through consecutive pairs of elements
10        for (int i = 0; i < nums.size() - 1; ++i) {
11            int currentDifference = nums[i + 1] - nums[i];
12          
13            if (currentDifference == prevDifference) {
14                // Continue the arithmetic sequence
15                ++currentLength;
16                // Add number of new arithmetic slices ending at position i+1
17                // When we extend a sequence by 1 element, we add 'currentLength' new slices
18                totalCount += currentLength;
19            } else {
20                // Start a new potential arithmetic sequence
21                prevDifference = currentDifference;
22                currentLength = 0;
23            }
24        }
25      
26        return totalCount;
27    }
28};
29
1/**
2 * Counts the total number of arithmetic slices in an array.
3 * An arithmetic slice is a subarray of at least 3 elements with constant difference between consecutive elements.
4 * 
5 * @param nums - The input array of numbers
6 * @returns The total count of arithmetic slices
7 */
8function numberOfArithmeticSlices(nums: number[]): number {
9    let totalSlices: number = 0;  // Total count of arithmetic slices found
10    let currentSliceCount: number = 0;  // Count of arithmetic slices ending at current position
11    let currentDifference: number = 3000;  // Current difference between consecutive elements (initialized to impossible value)
12  
13    // Iterate through the array comparing consecutive pairs
14    for (let i: number = 0; i < nums.length - 1; i++) {
15        const currentElement: number = nums[i];
16        const nextElement: number = nums[i + 1];
17        const difference: number = nextElement - currentElement;
18      
19        if (difference === currentDifference) {
20            // If difference matches the previous difference, we can extend the arithmetic sequence
21            currentSliceCount++;
22        } else {
23            // If difference changes, start a new potential arithmetic sequence
24            currentDifference = difference;
25            currentSliceCount = 0;
26        }
27      
28        // Add the count of new arithmetic slices ending at position i+1
29        totalSlices += currentSliceCount;
30    }
31  
32    return totalSlices;
33}
34

Time and Space Complexity

Time Complexity: O(n) where n is the length of the input array nums.

The algorithm iterates through the array once using pairwise(nums), which generates consecutive pairs of elements. For an array of length n, this produces n-1 pairs. Each iteration performs constant time operations:

  • Computing the difference b - a: O(1)
  • Comparison b - a == d: O(1)
  • Updating variables cnt, d, and ans: O(1)

Therefore, the overall time complexity is O(n-1) = O(n).

Space Complexity: O(1) - constant extra space.

The algorithm uses only a fixed number of variables:

  • ans: stores the total count of arithmetic slices
  • cnt: stores the current count of consecutive differences
  • d: stores the current common difference
  • a, b: temporary variables for the current pair

The pairwise() function returns an iterator that doesn't create a new data structure but generates pairs on-the-fly, so it doesn't consume additional space proportional to the input size.

Common Pitfalls

Pitfall 1: Incorrect Counter Reset Logic

The Problem: A common mistake is resetting the counter to 1 instead of 0 when encountering a new difference. This happens when developers think "we already have one pair with the new difference, so let's start counting from 1."

Incorrect Implementation:

for prev_num, curr_num in pairwise(nums):
    difference = curr_num - prev_num
  
    if difference == current_difference:
        consecutive_pairs += 1
    else:
        current_difference = difference
        consecutive_pairs = 1  # ❌ Wrong! Should be 0
  
    total_slices += consecutive_pairs

Why it's wrong: When we encounter a new difference, we've only seen ONE pair with that difference. To form an arithmetic subarray, we need at least TWO pairs with the same difference (creating 3 elements total). Setting consecutive_pairs = 1 would incorrectly count 2-element sequences as arithmetic subarrays.

Correct Solution: Always reset to 0 when starting a new potential sequence. The counter represents how many ADDITIONAL pairs we've found with the same difference after the first pair.

Pitfall 2: Off-by-One Error in Manual Iteration

The Problem: When not using pairwise(), developers often make indexing errors:

Incorrect Implementation:

for i in range(len(nums)):  # ❌ Goes out of bounds
    difference = nums[i+1] - nums[i]
    # ... rest of logic

Correct Solution:

for i in range(len(nums) - 1):  # ✓ Stops at second-to-last element
    difference = nums[i+1] - nums[i]
    # ... rest of logic

Pitfall 3: Misunderstanding the Count Accumulation

The Problem: Some developers mistakenly think they should add 1 to the answer whenever they find a valid arithmetic sequence continuation, rather than adding the current count value.

Incorrect Implementation:

if difference == current_difference:
    consecutive_pairs += 1
    total_slices += 1  # ❌ Wrong! Should add consecutive_pairs

Why it's wrong: When consecutive_pairs = k, it means we can form k different arithmetic subarrays ending at the current position:

  • If consecutive_pairs = 2, we have subarrays of length 3 and 4 ending here
  • We should add 2 to our total, not 1

Visual Example: For array [1,2,3,4,5]:

  • At position 3: we can form [1,2,3] (count = 1)
  • At position 4: we can form [2,3,4] AND [1,2,3,4] (count = 2)
  • At position 5: we can form [3,4,5], [2,3,4,5], AND [1,2,3,4,5] (count = 3)

The accumulation pattern follows the triangular number sequence, which is why we add the current count value each time.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

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

Load More