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.