Incorrect Arithmetic Slices Solution
创建于:2025年1月24日
创建于:2025年1月24日
Look at this solution for the Leetcode problem 413 Arithmetic Slices and explain why it's really incorrect:
func numberOfArithmeticSlices(nums []int) int {
res := 0
if len(nums) < 3 {
return 0
}
start := 0
diff := nums[1] - nums[0]
for i, j := 1, 0; i <= len(nums); i, j = i+1, j+1 {
if i == len(nums) {
n := i - start - 2
res += n * (n + 1) / 2
break
}
if nums[i]-nums[j] != diff {
if i < len(nums)-1 {
diff = nums[i] - nums[i-1]
}
n := i - start - 2
res += n * (n + 1) / 2
start = i
}
}
return res
}
First, here’s a quick restatement of what the code is trying to do:
diff
) based on the last two elements in the sequence.At a glance, the logic looks similar to well-known solutions. However, there is a subtle but critical bug in how it updates the start of the next arithmetic run when it detects a mismatch of differences. This causes the function to miss some valid arithmetic sub-slices.
Inside the if nums[i] - nums[j] != diff { ... }
branch, the code does:
gon := i - start - 2 res += n * (n + 1) / 2 // closes out the old arithmetic run start = i // starts the new run at i diff = nums[i] - nums[i-1]
start = i
wrong?When a difference changes at index i
, that means the previous run definitely ends at i - 1
. But the next arithmetic run might need to begin at i - 1
, not i
. After all, nums[i-1]
and nums[i]
form a new pair with a fresh difference that could continue forward.
By setting start = i
, you effectively throw away the chance to form an arithmetic sub-slice that begins one step earlier at i-1
. As soon as there is a difference change, the code “jumps forward” too far, missing any longer runs that should include the element at i-1
.
Consider the array:
nums = [1, 2, 3, 5, 7, 9]
Differences across the array:
text(2 - 1) = 1 (3 - 2) = 1 (5 - 3) = 2 <-- difference changes here (7 - 5) = 2 (9 - 7) = 2
Let’s see what the code does step-by-step:
Indices 0..2: We have an arithmetic run [1,2,3]
with difference = 1.
i = 3
, it detects nums[3] - nums[2] = 5 - 3 = 2
, which is not the old diff=1
. So it closes the run.3 - 0 = 3
. Using the formula n = i - start - 2
, we get n = 3 - 0 - 2 = 1
. It adds 1 * (1+1)/2 = 1
to res
(that correctly accounts for the single 3-length slice [1,2,3]
).start = 3
, diff = 5 - 3 = 2
.Indices 3..5: Now it looks at [5,7,9]
as the new run.
res
.But the actual arithmetic sub-slices in [1,2,3,5,7,9]
are:
[1,2,3]
[5,7,9]
[3,5,7,9]
(difference = 2 throughout)[3,5,7]
is also valid.The code completely skips counting [3,5,7]
and [3,5,7,9]
because once it “closed out” the run at index 3, it started fresh at start = 3
. It never revisited the possibility that the sub-slice beginning at index 2 (nums[2] = 3
) could continue forward with the new difference 2.
In other words, by resetting start = i
, the code fails to account for any new arithmetic run that might begin just one step earlier (at i - 1
).
A proper solution typically does one of two things:
Track the length of the current arithmetic run dynamically.
nums[i] - nums[i-1] == nums[i-1] - nums[i-2]
, increment a count for the “current run” length. Otherwise reset it.Use a “start index” but reset it more carefully.
i
, you close out the run ending at i - 1
.start = i - 1
so that the new run starts from that last element.Either way, you avoid skipping the potential sub-slice that can start just before the mismatch.
Even though the triangular formula for counting sub-slices within a run is correct (n*(n+1)/2
for n = length-2
), the place where the code resets start
is incorrect. It loses out on valid arithmetic sub-slices that begin one step earlier than i
. That’s why it undercounts and is “really incorrect” for certain inputs.