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.
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:
-
Iterate through adjacent pairs: Using
pairwise(nums)
, we examine each pair of consecutive elements(a, b)
. -
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 positionb
)
- Increment
- 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
- Update
- If
-
Accumulate the count: The key insight is that when
cnt = k
, it means we've foundk
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)
- When
Example trace with nums = [1,2,3,4]
:
- Pair
(1,2)
: difference = 1, new difference, setd=1
,cnt=0
,ans=0
- Pair
(2,3)
: difference = 1 =d
, incrementcnt=1
, add toans=0+1=1
- Pair
(3,4)
: difference = 1 =d
, incrementcnt=2
, add toans=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 EvaluatorExample 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
, andans
: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 slicescnt
: stores the current count of consecutive differencesd
: stores the current common differencea, 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.
How does merge sort divide the problem into subproblems?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
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
Want a Structured Path to Master System Design Too? Don’t Miss This!