413. Arithmetic Slices

95 阅读2分钟

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

首先容易观察到:

  1. 长度为3的等差数列,可以贡献1种答案。例如 [1,2,3] 。
  2. 长度为4的等差数列,可以贡献3种答案。例如[1,2,3,4],有长度为3的子数列[1,2,3][2,3,4]两种。以及长度为4的数列[1,2,3,4]一种。一共是1+2=3种。
  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清零。