leetcode:等差数列划分

题目来源:力扣

题目介绍

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
========================================================
示例:
A = [1, 2, 3, 4]
返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。
=======================================================

审题:

对于这道题, 我们首先提供一种易于理解, 但时间复杂度较高的方法.
我们可以计算出所有子序列S[i, j]是否是等差数列, 然后累计其中为等差数列的个数. 如果我们已知子序列S[i, j]是否是等差数列, 则不能判断子序列S[i, j+1]是否是等差数列. 因此, 我们可以先计算所有长度为3的子序列是否是等差数列, 然后一次计算所有长度为4, …5, n的子序列是否是等差数列.

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        boolean[][] S = new boolean[A.length][A.length];

        int total = 0;
        //basecase , 判断长度为3的数列是否是等差数列
        for(int i = 0; i < A.length-2; i++){
            if(A[i+1]-A[i] == A[i+2]-A[i+1]){
                S[i][i+2] = true;
                total++;
            }
        }

        for(int len = 4; len <= A.length; len++){
            for(int i = 0; i <= A.length-len; i++){
                int j = i + len -1;
                if(S[i][j-1] && A[j]-A[j-1] == A[j-1]-A[j-2]){
                    S[i][j] = true;
                    total++;
                }
                else
                    S[i][j] = false;
            }
        }
        return total;
    }
}

复杂度分析

不难看出, 该方法时间复杂度与空间复杂度均为O(N2)O(N^2).

实际上, 为了计算所有等差数列的个数, 我们可以优化上述代码, 将空间复杂度由O(N2)O(N^2)降为O(1)O(1). 如果当前A[i, j]为等差数列, 则我们可以往下判断A[i, j+1]是否是等差数列, 如果A[i, j]不是等差数列, 则A[i, j+1], …A[i, n]都不是等差数列.

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        int total = 0;
        for(int i = 0; i < A.length-2; i++){
            for(int len = 2; len < A.length-i; len++){
                if(A[i+len]-A[i+len-1] == A[i+len-1] - A[i+len-2])
                    total++;
                else
                    break;
            }
        }
        return total;
    }
}

动态规划算法

考虑, 如果我们已知以A[i]结尾的等差数列的个数, 我们能否推导出以A[i+1]结尾的等差数列的个数. 事实上非常容易, 如果A[i+1] - A[i] = A[i]-A[i-1], 那么所有以A[i+1]结尾的等差数列此时在增加了一个元素A[i+1]后仍然是等差数列, 而A[i-1], A[i], A[i+1]此时也构长一个等差数列, 因此以A[i+1]结尾的等差数列的个数等于以A[i]结尾的等差数列的个数+1. 而如果A[i+1]-A[i] != A[i]-A[i-1], 则以A[i+1]结尾的等差数列个数为1.
我们不难写出该动态规划算法的实现:

class Solution {
    public int numberOfArithmeticSlices(int[] A) {
        //S[j]表示以j结尾的等差序列个数
        //如果A[j+1] - A[j] = A[j] - A[j-1] , S[j+1] = S[j] + 1
        //else S[j+1] = 0;
        if(A.length < 3)
            return 0;
        int total = 0;
        int[] S = new int[A.length];
        if(A[2]-A[1] == A[1]-A[0]){
            S[2] = 1;
            total += S[2];
        }

        for(int i = 3; i < A.length; i++){
            if(A[i] - A[i-1] == A[i-1] - A[i-2]){
                S[i] = S[i-1] + 1;
                total += S[i];
            }
        }
        return total;
    }
}

不难分析发现, 该算法的时间复杂度与空间复杂度均为O(N)O(N).由于以A[i+1]结尾的等差数列个数只有以A[i]结尾的等差数列个数相关, 因此我们可以考虑进行状态压缩, 将空间复杂度降为O(1)O(1)

class Solution {
    public int numberOfArithmeticSlices(int[] A) {

        if(A.length < 3)
            return 0;
        int total = 0;
        int prev = 0;
        if(A[2]-A[1] == A[1]-A[0]){
            prev = 1;
            total += prev;
        }

        for(int i = 3; i < A.length; i++){
            if(A[i] - A[i-1] == A[i-1] - A[i-2]){
                prev = prev + 1;
                total += prev;
            }
            else
                prev = 0;
        }
        return total;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章