这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
413. 等差数列划分
如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。
- 例如,
[1,3,5,7,9]、[7,7,7,7]和[3,-1,-5,-9]都是等差数列。
给你一个整数数组 nums ,返回数组 nums 中所有为等差数组的 子数组 个数。
子数组 是数组中的一个连续序列。
示例 1:
输入:nums = [1,2,3,4]
输出:3
解释:nums 中有三个子等差数组:[1, 2, 3]、[2, 3, 4] 和 [1,2,3,4] 自身。
示例 2:
输入:nums = [1]
输出:0
提示:
1 <= nums.length <= 5000-1000 <= nums[i] <= 1000
方法一
仔细读题很重要,子数组是连续的序列;一开始没留意,就想不到方法;
根据题意,既然要求的子数组是一个连续的序列,那么我们可以这么做:
- 枚举每一个元素
- 以当前枚举到的元素作为子数组的第一个元素
- 找到以当前元素为头的最长子数组,例如长度为
k - 那么长度大于3的子数组就会有
k,k-1,k-2,...,1,数学公式求和,首项加尾项乘以项数除以2 - 将i移到尾元素,保证每个元素被枚举一次,O(n)的时间复杂度
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int n = nums.length;
if (n < 3) return 0;
int res = 0;
for (int i = 1; i < n; i ++ ) {
int len = nums[i] - nums[i - 1];
int j = i;
while(j < n && nums[j] - nums[j - 1] == len) j ++;
int k = j - i + 1;
if (k >= 3) res += (k - 2 + 1) * (k - 2) / 2;
i = j - 1;
}
return res;
}
}
时间复杂度: O(n)
空间复杂度: O(1)
方法二
动态规划:
-
f[i]表示以i为结尾的等差且长度大于3的子数组个数 -
维护一个
k,表示当前的等差数组的差值,k=2200表示当前没有构成等差数组,(大于2000即可) -
状态转移:
- 如果构成了等差数组(k != 2200),判断当前元素减去上一个元素是否等于
k,f[i]=f[i]-f[i-1]==k?f[i-1]+1:0 - 如果没有构成等差数组(k==2200),判断当前元素和他的前两个元素能否构成等差数组
- 如果构成了等差数组(k != 2200),判断当前元素减去上一个元素是否等于
-
最后将所有
f[i]相加即是答案
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int n = nums.length;
if (n < 3) return 0;
int[] f = new int[n];
int k = 2200;
for (int i = 0; i < n; i ++ ) {
if (i < 2) f[i] = 0;
else {
if (k > 2100) {
if (nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) {
f[i] = 1;
k = nums[i] - nums[i - 1];
}
else f[i] = 0;
}
else {
if (nums[i] - nums[i - 1] == k) f[i] = f[i - 1] + 1;
else {
f[i] = 0;
k = 2200;
}
}
}
}
int res = 0;
for (int i = 0; i < n; i ++ )
res += f[i];
return res;
}
}
时间复杂度: O(n)
空间复杂度: O(n)
状态转移只和前一个f[i]相关,所以空间复杂度能优化成常数