An integer array is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.
- For example,
[1,3,5,7,9],[7,7,7,7], and[3,-1,-5,-9]are arithmetic sequences.
Given an integer array nums, return the number of arithmetic subarrays of nums.
A subarray is a contiguous subsequence of the array.
Example 1:
Input: nums = [1,2,3,4]
Output: 3
Explanation: We have 3 arithmetic slices in nums: [1, 2, 3], [2, 3, 4] and [1,2,3,4] itself.
Example 2:
Input: nums = [1]
Output: 0
答案
易于理解的版本,但效率较差
func numberOfArithmeticSlices(nums []int) int {
if len(nums) < 3 {
return 0
}
res := 0
left := 0
right := 1
for n := len(nums); right < n; {
diff := nums[right] - nums[left]
//因为left=right-1,所以第一次循环时,nums[right]-nums[right-1] == diff肯定成立
//right的值至少会加一,所以不用担心死循环
for right < n && nums[right]-nums[right-1] == diff {
right++
}
if width := right - left; width >= 3 {
//遍历子序列长度
for i := 3; i <= width; i++ {
res += width - i + 1
}
}
//重新定义左区间,为什么不是left = right?
//因为 1,2,3,5,7
//1,2,3是等差序列,但3,5,7也是等差序列
//所以当确定了一个等差序列的区间时,下一次的循环左区间起点应该是上一次等差区间的最后一个元素
left = right - 1
}
return res
}
效率好的版本,但较难理解
func numberOfArithmeticSlices(A []int) int {
res := 0
curr := 0
for i, n := 2, len(A); i < n; i++ {
if A[i]-A[i-1] == A[i-1]-A[i-2] {
curr++
res += curr
}else {
curr = 0
}
}
return res
}
解释: 没整明白的朋友看这里:
举例
[1, 2, 3, 4, 5, 19, 20, 30, 40]。答案为7。
首先容易观察到:
- 长度为3的等差数列,可以贡献1种答案。例如
[1,2,3]。 - 长度为4的等差数列,可以贡献3种答案。例如
[1,2,3,4],有长度为3的子数列[1,2,3]和[2,3,4]两种。以及长度为4的数列[1,2,3,4]一种。一共是1+2=3种。 - 长度为5的等差数列,可以贡献6种答案。例如
[1,2,3,4,5],有长度为3的子数列[1,2,3]和[2,3,4]和[3,4,5]三种,以及长度为4的子数列[1,2,3,4]和[2,3,4,5]两种,以及长度为5的数列[1,2,3,4,5]一种。一共是1+2+3=6种。
假设我们已经找到了一个长度为3的等差数列。它可以给答案带来一种贡献。
如果遍历到下一个数时,发现这个数可以拼接到前面长度为3的等差数列的末尾,形成一个长度为4的等差数列,那么把长度为3的等差数列的答案贡献数加一,就是由于这次拼接带来的新的贡献数。当前长度为4的等差数列,这次拼接新的贡献量为1+1=2。
同理,下一次遍历又发现一个数可以在已发现的长度为4的等差数列的基础上,拼接成长度为5的等差数列,那么新的贡献量就是2+1=3.
如果下一个数无法与前面的数列行成新的等差数列,那么贡献量curr清零。