leetcode446. Arithmetic Slices II - Subsequence

题目要求

A sequence of numbers 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, these are arithmetic sequences:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic.

1, 1, 2, 5, 7
 
A zero-indexed array A consisting of N numbers is given. A subsequence slice of that array is any sequence of integers (P0, P1, ..., Pk) such that 0 ≤ P0 < P1 < ... < Pk < N.

A subsequence slice (P0, P1, ..., Pk) of array A is called arithmetic if the sequence A[P0], A[P1], ..., A[Pk-1], A[Pk] is arithmetic. In particular, this means that k ≥ 2.

The function should return the number of arithmetic subsequence slices in the array A.

The input contains N integers. Every integer is in the range of -231 and 231-1 and 0 ≤ N ≤ 1000. The output is guaranteed to be less than 231-1.

 
Example:

Input: [2, 4, 6, 8, 10]

Output: 7

Explanation:
All arithmetic subsequence slices are:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]

从一个无序的整数数组中,找到所有等差子数列的数量。这里需要注意,等差子数列要求从原数组中找出Pk个下标的元素,其中P0 < P1 < P2... < Pk,且满足A[P1]-A[P0] = A[P2] - A[P1] ... = A[Pk]-A[Pk-1]

1,3,7,51,3,5是等差子数组,但是1,3,5,7不是,因为5和7的相对顺序变了。

思路和代码

这里主要是对高赞答案的中文翻译,这个答案太牛了,也让我对动态规划类型的题目有了全新的思考方式。可见算法题不再多,在于算法思维的构建啊。

原答案链接

这个博主首先确定了使用子问题的答案构成主问题答案的核心思路,即假设要想知道[0,n]的数组中等差子数组的个数,可以通过计算出[0,n-1]的子数组中等差子数组的个数。假设将计算[0,k]的子数组中的等差子数组的个数声明为P(k),则需要从P(n-1)推导出P(n)的结果。

现在思考一下假如想要求出P(n),我们需要哪些信息。对于任意0<=j<n,我们需要知道以A[j]作为结尾且间隔为d=A[n]-A[j]的等差数列的个数。但是P(j)这个函数只提供了以A[j]为结尾的所有等差数列的个数,因此只以P(j)作为推导函数是不够的,需要重新建立基础问题函数。

结合上面的分析,新的函数P(k, d)记录了以A[k]为结尾,且等差距离为d的等差子数组的数量。则可以推导出如下的递推公式:

P(0, d) = 0 d为任意非负整数
P(i, d) = P(j, d) + 1 其中A[i]-A[j]=d && 0<= i < j

P(0,d)的推导很好理解,即对于任意间隔的等差数列,以A[0]为结尾的等差数列的个数一定为0。
P(i, d) = P(j, d) + 1也很好理解,即以A[i]为结尾的等差数列的个数等于以A[j]为结尾的等差数列的个数。但是这里加一其实是为了解决一个问题,即潜在的等差数列的问题。因为可能存在一种情况,如1,2,3,则除了1,2,3,4可以构成等差数列,2,3,4也可以构成一个等差数列。因此P(i,d)会记录所有潜在的等差数列的个数。

在理清楚思路之后,就开始决定如何在代码层面使用具体的数据结构来进行数据的存取。这里采用长度为A.length的Map数组来存储以A[i]为结尾的所有间隔的等差子数组的个数,因此Map中的key为间隔的长度。代码如下:

    public int numberOfArithmeticSlices(int[] A) {
        if (A.length < 3) return 0;
        int result = 0;
        Map<Long, Integer>[] maps = new Map[A.length];
        for (int i = 0 ; i < A.length ; i++) {
            maps[i] = new HashMap<>();
            for (int j = i - 1 ; j >= 0 ; j--) {
                long diff = (long)A[i] - A[j];
                int c1 = maps[i].getOrDefault(diff, 0);
                int c2 = maps[j].getOrDefault(diff, 0);
                result += c2;
                maps[i].put(diff, c1 + c2 + 1);
            }
        }
        return result;
    }

在提交之后还有一个耗时特别短的答案,但是这个答案没看懂,就不多赘述了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章