LeetCode每日一题 413. 等差数列划分

163 阅读2分钟

这是我参与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的子数组就会有kk-1k-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)

image-20210810090630511

方法二

动态规划:

  • f[i]表示以i为结尾的等差且长度大于3的子数组个数

  • 维护一个k,表示当前的等差数组的差值,k=2200表示当前没有构成等差数组,(大于2000即可)

  • 状态转移:

    • 如果构成了等差数组(k != 2200),判断当前元素减去上一个元素是否等于kf[i]=f[i]-f[i-1]==k?f[i-1]+1:0
    • 如果没有构成等差数组(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]相关,所以空间复杂度能优化成常数

image-20210810092114201