给你一个整数数组 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] 的一个子序列。
示例 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]
解题思路:该题可以使用动态规划进行求解,d=nums[i]-num[j], dp[i][d] = dp[i][d] + dp[j][d] + 1 ;指的是可以跟{num[i],num[j]}一起组等差子序列的个数,即最后一项为 nums[i],公差为 d 的等差子序列的个数。
代码实现时,由于nums[i] 的范围很大,所以计算出的公差的范围也很大,我们可以将状态转移数组dp的第二维用哈希表 f 代替。
另外,如果nums[i]−nums[j]=d,那么在这一轮遍历中,j 会遍历到与上一轮相同的位置,答案增加的次数相同,并且额外多出了j的{各个等差子序列,nums[i+1]} 这些等差数列,因此有:
等差子序列总数ans = ans + dp[j][d];
public int numberOfArithmeticSlices(int[] nums) {
int ans = 0;
int l = nums.length;
Map<Long, Integer>[] f = new Map[l]; // 状态转移表
for(int i=0; i<l; i++) {
f[i] = new HashMap<Long, Integer>(); //初始化 状态转移表
}
for(int i=0; i<l; i++) {
for(int j=0; j<i; j++) {
Long d = 1L * nums[i] - nums[j]; //得到公差
int cnt = f[j].getOrDefault(d, 0); //得到dp[j][d]的值
ans += cnt; // 等差子序列总数
f[i].put(d, f[i].getOrDefault(d, 0) + cnt + 1); // dp[i][d] = dp[i][d] + dp[j][d] + 1
}
}
return ans;
}