LeetCode413:等差数列划分
动态规划
题目
如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。
例如,[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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/arithmetic-slices
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
来源:力扣(LeetCode)
解题思路
又是数组又是连续的,正好这几天做的动态规划多了,很自然的想到dp数组。
由等差数列可知一个条件:当nums[i]-nums[i-1]=nums[i-1]-nums[i-2]时,可以构成一个连续的最短数列,包含数组中三个连续的数。
按照动态规划的思想来看,就是遍历nums,观察以当前nums[i]结尾的子数组有几个等差数列,这就是dp[i]代表的值。
接下来以nums={1,2,3,4,5,6}为例解释一下我的思路:
因为要求最短的子数组长度为3,我们从nums[2]开始遍历,根据nums[i]-nums[i-1]=nums[i-1]-nums[i-2]条件判断可知以nums[2]结尾的子数组有1个等差数列(1,2,3),dp[2]=1。
接下来循环到nums[3],这里要说一个小细节,我们要维护一个变量flag作为在循环到nums[i]时nums[i]与之前的数构成的新的等差数列(除去【nums[i],nums[i-1],nums[i-2]】),之前求最长递增序列时如果满足条件dp[i]加上的值是固定的,但是在这里这个加值会改变,我们要找到改变的规律。
flag最开始等于0,先判断当前数与前两个数,组成的子数组是否是一个等差数列,也就是nums[i]-nums[i-1]=nums[i-1]-nums[i-2]条件,如果是则当前数可以和之前的等差数列一起构成等差数组,但是这时候的dp[i]=flag+1,1代表本次得到的最短等差数列,之后令flag++,不是则说明数列的连续性中断了,更改flag=0。
如图当nums={1,2,3,4,5,6}时:
所以最终有10个连续等差数列。
但是当nums={1,2,3,9,4,5,6}时:
因为中间这个9令3和4的连续断开了,在nums[i]=9时flag归零,最终有两个连续等差数列:{1,2,3}和{4,5,6}
代码实现
在代码中我用一个整型变量num来代替了dp数组。
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
int num=0;
//辅助变量,代表循环中每一次新加入的数和之前的等差数列一起可以新构成的等差数列个数
int flag=0;
if (nums.length<3) {
return 0;
}
for (int i = 2; i < nums.length; i++) {
//判断当前数与前两个数,组成的子数组是否是一个等差数列,如果是则当前数可以和之前的等差数列一起构成等差数组,不是则说明数列的连续性中断了,更改flag
if (nums[i]-nums[i-1]==nums[i-1]-nums[i-2]) {
//新构成的等差数列=flag的数量+当前数与前两个数,组成的子数组
num+=(flag+1);
flag++;
}else {
flag=0;
}
}
return num;
}
}
总结
对算法和数学的结合还不够敏锐,动态规划这一块可以结合LeetCode1713得到子序列的最少操作次数这道题里求最长连续子序列的思想学习。