给你一个整数数组 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
解释:数组中的任意子序列都是等差子序列。
题干要求的是等差子序列,序列是不连续的。
我们可以使用两个for循环,第一个for循环固定等差子序列的最后一项,第2个for循环固定等差子序列的倒数第二项(枚举等差子序列的倒数第2项),类似于[……,nums[j],nums[i]],最后两项固定了,那么他们的等差值肯定是确定了,然后在根据等差值往前查找。代码如下
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
//等差子序列的个数
int count = 0;
int length = nums.length;
//map数组,其中map[i]中key和value分别是以num[i]为
//倒数第二项的等差子序列的等差值和等差子序列个数
Map<Long, Integer>[] map = new Map[length];
map[0] = new HashMap<>();
//固定等差子序列的最后一项
for (int i = 1; i < length; i++) {
map[i] = new HashMap<>();
//枚举等差子序列的倒数第2项
for (int j = i - 1; j >= 0; j--) {
//等差子序列的后两项,[……,nums[j],nums[i]]。
// 由于等差值可能出现越界,先转换为long类型
long diff = (long) nums[i] - nums[j];
//在int范围内,等差值不能太大
if (diff <= Integer.MIN_VALUE || diff >= Integer.MAX_VALUE)
continue;
//等差子序列的最后两项固定了,那么他们的等差值也就确定了,前面的
//我们只需要从map中查找即可
//count_j是以nums[i]为等差子序列最后一项的等差子序列个数,nums[j]是
//倒数第2项,类似于[……,nums[j],nums[i]]
int count_j = map[j].getOrDefault(diff, 0);
//统计所有以nums[i]为等差子序列最后一项的等差子序列个数
count += count_j;
//保存以nums[i]为倒数第二项的等差子序列个数
map[i].put(diff, map[i].getOrDefault(diff, 0) + count_j + 1);
}
}
return count;
}
}
上面代码有两个注意点
等差数列的长度至少是3,所以等差值不可能超过Integer.MAX_VALUE和小于Integer.MIN_VALUE,即使[Integer.MIN_VALUE,0,Integer.MAX_VALUE]也不可能,所以如果等差值超过这个范围,说明不可能构成等差数列,直接跳过。
map[i]中的key和value分别记录的是以num[i]为倒数第2项构成的等差数列的等差值和个数。这是因为这样做我们可以过滤掉只有两个元素的等差数列。
我们首先考虑至少有两个元素的等差子序列,下文将其称作弱等差子序列。
由于尾项和公差可以确定一个等差数列,因此我们定义状态 f[i][d] 表示尾项为 nums[i],公差为 d 的弱等差子序列的个数。
我们用一个二重循环去遍历 nums 中的所有元素对 (nums[i],nums[j]),其中 j
f[i][d]+=f[j][d]+1
由于题目要统计的等差子序列至少有三个元素,我们回顾上述二重循环,其中「将 nums[i] 加到以 nums[j]为尾项,公差为 d 的弱等差子序列的末尾」这一操作,实际上就构成了一个至少有三个元素的等差子序列,因此我们将循环中的 f[j][d] 累加,即为答案。
代码实现时,由于 nums[i]的范围很大,所以计算出的公差的范围也很大,我们可以将状态转移数组 f的第二维用哈希表代替。
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int ans = 0;
int n = nums.length;
Map<Long, Integer>[] f = new Map[n];
for (int i = 0; i < n; ++i) {
f[i] = new HashMap<Long, Integer>();
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
long d = 1L * nums[i] - nums[j];
int cnt = f[j].getOrDefault(d, 0);
ans += cnt;
f[i].put(d, f[i].getOrDefault(d, 0) + cnt + 1);
}
}
return ans;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence/solution/deng-chai-shu-lie-hua-fen-ii-zi-xu-lie-b-77pl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
好难好难好难好难好难好难好难好难好难好难好难