# 446. 等差数列划分 II - 子序列 (Hard)

## 问题描述

[446. 等差数列划分 II - 子序列][link] (Hard)

[link]: https://leetcode.cn/problems/arithmetic-slices-ii-subsequence/

给你一个整数数组 `nums` ，返回 `nums` 中所有 **等差子序列** 的数目。

如果一个序列中 **至少有三个元素** ，并且任意两个相邻元素之差相同，则称该序列为等差序列。

- 例如， `[1, 3, 5, 7, 9]`、 `[7, 7, 7, 7]` 和 `[3, -1, -5, -9]` 都是等差序列。
- 再例如， `[1, 1, 2, 5, 7]` 不是等差序列。

数组中的子序列是从数组中删除一些元素（也可能不删除）得到的一个序列。

- 例如， `[2,5,10]` 是 `[1,2,1,2,4,1,5,10]` 的一个子序列。

题目数据保证答案是一个 **32-bit** 整数。

**示例 1：**

```
输入：nums = [2,4,6,8,10]
输出：7
解释：所有的等差子序列为：
[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]
```

**示例 2：**

```
输入：nums = [7,7,7,7,7]
输出：16
解释：数组中的任意子序列都是等差子序列。
```

**提示：**

- `1  <= nums.length <= 1000`
- `-2³¹ <= nums[i] <= 2³¹ - 1`

## 解题思路

这题很明显需要使用动态规划来解决，例如以 $dp[i]$ 表示以 `nums[i]` 结尾的子序列的长度，那么要如何从 $dp[j]$ 转移过来呢？要从 $dp[j]$ 转移过来，我们必须知道 $nums[i] - nums[j]$，因此，$dp$ 需要两个维度，第一个维度表示数组 $nums$ 的索引，第二个维度表示差 $diff$，由于 $diff$ 可能很大，还可能为负数，所以我们采用哈希表来记录。

因此 `vector<unordered_map<long, long>> dp`。

此外，还有一个问题，那就是题目要求数组长度至少为 $3$，而要在状态转移过程中排除掉长度为 $2$ 的序列数是比较麻烦的，其实我们可以等计算完之后再去除长度为 $2$ 的队列，即减去 $\binom{n}{2}$。

## 代码

```cpp
class Solution {
  public:
    int numberOfArithmeticSlices(vector<int> &nums) {
        if (nums.size() < 3) {
            return 0;
        }
        int n = nums.size();
        int res = 0;
        vector<unordered_map<long, long>> dp(n);
        for (int i = 1; i < n; ++i) {
        	for (int j = 0; j < i; ++j) {
        		long diff = (long)nums[i] - nums[j];
        		dp[i][diff] += 1 + dp[j][diff];
        		res += 1 + dp[j][diff];
        	}
        }
        return res - n * (n - 1) / 2;
    }
};
```


