概述
- 注意:题目要求连续子数组个数至少有3个元素,但是数据只确保>=1,所以可以特殊处理一下
分析
暴力解
枚举所有长度大于 2 的连续子数组,如果是等差数列,给计数器加 1
双指针——滑动窗口法
左指针指向所有包含该元素的符合条件的等差子数组
右指针开始移动,确定等差数组的差值,只有后面一个数字满足该差值,就可以组成一个子数组,结果加1
如果右指针指向的数字不满足之前的差值 或 指向的数组的末尾,则说明包含左指针元素的所有等差子数组已经找完,
于是移动左指针,继续操作
动态规划
不同的dp[i]含义,有不同的动态规划方法
dp[i]:表示[0,i]的元素中,符合条件的结果数
- dp[i] = dp[i -1] + {加入nums[i]后可以增加的结果数}
dp[i]:表示所有以nums[i]结尾的子数组满足条件的结果数
if(nums[i] - nums[i-1] == nums[i-1] - nums[i - 2])
,dp[i] = dp[i - 1] + 1
思路
暴力解
- 如何枚举所有的组合?
- 两层循环枚举所有连续数组,内层还要一个循环判断是否为等差数列
- 时间复杂度是 O ( n 3 ) O(n^3) O(n3),代码略
双指针——滑动窗口法
- 套用模版就行
动态规划
方法1:
-
关键在于如何确定【加入nums[i]后可以增加的结果数】这一部分的值
-
我们可以注意到
1 2 ---> dp[2] = 0 1 2 3 ---> dp[3] = 1 = dp[2] + 1; 1 2 3 4 ---> dp[4] = 3 = dp[3] + 2; 1 2 3 4 5 ---> dp[5] = 6 = dp[4] + 3; 1 2 3 4 5 7 ---> dp[6] = 6 = dp[5] + 0;
也就是说,如果是一个连续增加的值,那么没增加一个值,相较于前一个dp,加入num[i]后增加的结果数是递增的;
但是如果一旦不连续,则增加的值需要清空重新计算
方法2:
- 直接编写代码就行
代码
双指针——滑动窗口法
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& A) {
int n = A.size();
int res = 0; // 用来保存结果
for (int i = 0; i < n - 2; i++) { // i是左指针
int d = A[i + 1] - A[i]; // 记录如果包含nums[i]的连续子数组需要满足的差值
for (int j = i + 1; j < N - 1; j++) { // 右指针开始移动
if (A[j + 1] - A[j] == d) // 只要满足,就增加结果
res ++;
else
break;
}
}
return res;
}
};
动态规划——法1
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
if (nums.size() < 3) return 0;
vector<int> dp(nums.size() + 1);
dp[1] = 0;
dp[2] = 0;
int inc = 1;
for (int i = 3; i <= nums.size(); ++i) {
if (nums[i - 1] - nums[i - 2] == nums[i - 2 ] - nums[i - 3]) {
dp[i] = dp[i - 1] + inc;
++inc; // 关键在于inc
}
else { // 一旦不连续,则inc重新从1开始
dp[i] = dp[i - 1];
inc = 1;
}
}
return dp[nums.size()];
}
};
动态规划——法2
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n = nums.size();
if (n < 3) return 0;
vector<int> dp(n, 0);
for (int i = 2; i < n; ++i) {
if (nums[i] - nums[i-1] == nums[i-1] - nums[i-2]) {
dp[i] = dp[i-1] + 1;
}
}
return accumulate(dp.begin(), dp.end(), 0); // 最后要累积所有结果
}
};
这种动态规划思想,有点类似双指针的想法。
只是双指针设定为起始节点,而该动态规划设定为结尾结点