题目链接:
不想戳的看下图:
示例:
提示:
解题思路1:
双指针
1:先求出所有满足等差要求的最长连续子数组。
2:在最长子数组中,找出所有满足要求的连续分子数组,其数量和即为所求值。
代码如下:
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int result = 0;
if (nums.length < 3) {
return result;
}
//求所有最长连续子数组
List<int[]> sonMaxLongList = new ArrayList<>();
int start = 0;
int end = 0;
for (int i = 0; i < nums.length - 2; i++) {
int a = nums[i];
int b = nums[i + 1];
int c = nums[i + 2];
//满足条件,则结尾指针指向i+2
if ((b * 2) == (a + c)) {
end = i + 2;
//如果结尾是数组末尾,压入
if (end == nums.length - 1) {
sonMaxLongList.add(new int[]{start, end});
}
} else {
//不满足条件,有两种情况,一种是前面有满足条件的最长子数组、一种是都不满足
if(end-start >= 2){
//满足条件压入,并将开始指针置于结尾。i指针直接跳到结尾前一位。
sonMaxLongList.add(new int[]{start, end});
start = end;
i = start-1;
}else{
start = i + 1;//开始指针指向下一个循环值
}
}
}
//找到所有满足条件的子数组可能
if(sonMaxLongList.size() != 0){
for(int[] son : sonMaxLongList){
result += getNumberOfArithmeticSlicesTimes(son[0], son[1]);
}
}
return result;
}
public int getNumberOfArithmeticSlicesTimes(int start, int end) {
int times = 0;
for (int i = start; i <= end-2; i++) {
for(int j = i+2; j <= end; j++){
times++;
}
}
return times;
}
}
解题思路2:
动态规划(dp)
状态:dp[i] 表示以 nums[i] 结尾的数组可以组成等差数列的个数,并不一定以 nums[0] 开头。
状态转移方程:dp[i] = dp[i-1]+1 if nums[i]-nums[i-1]==nums[i-1]-nums[i-2] else 0
可以理解为:
1、若之前的dp不为0,且当前nums[i]符合条件,则说明此 nums[i] 与前面以nums[i-1]结尾的等差序列是连续的(有相同的差值)。假设连续的等差序列第一个数为num[a],则以nums[i-1]结尾的等差数列,均可以换为以nums[i]结尾,这就等于dp[n-1]的值,而加一是因为nums[i]的引入又增加了nums[a]至nums[i]这个等差数列。
2、若dp为0,则说明之前并没有以nums[i-1]结尾的等差序列存在,此时以nums[i]结尾的是新的等差数列,所以就是1。
3、若条件不满足,则说明此时的nums[i]不连续,与之前的等差数列存在间隙,所以为0,表示以nums[i]为结尾的等差数列个数为0。
最后将所有的dp加起来就是ans。
代码如下:
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int len = nums.length;
int[] dp = new int[len];
for(int i=2; i<len; i++){
if(nums[i]-nums[i-1] == nums[i-1]-nums[i-2]){
dp[i] = dp[i-1] + 1;
}else{
dp[i] = 0;
}
}
int ans = 0;
for(int i=0; i<len; i++){
ans += dp[i];
}
return ans;
}
}
小结
两种方法都比较简单,而且速度都很快,都是好方法。若同学们有更好的方法请私我,谢谢谢谢谢!