【LeetCode】413. Arithmetic Slices 解题报告(Python &C++)

作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


目录

    • 题目描述
    • 题目大意
    • 解题方法
      • 暴力判断
      • 暴力判断的优化
      • 递归解法
      • 动态规划
    • 日期

题目地址:https://leetcode.com/problems/arithmetic-slices/description/

题目描述

A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.

For example, these are arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic.

1, 1, 2, 5, 7

A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N.

A slice (P, Q) of array A is called arithmetic if the sequence:
A[P], A[p + 1], …, A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q.

The function should return the number of arithmetic slices in the array A.

Example:

A = [1, 2, 3, 4]

return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

题目大意

这道题的题目不是一般的长,其实就是一个意思:给你一串数字,返回这串数字中能够构成等差数列的子串的数目。

解题方法

暴力判断

判断每个开始和结尾的子数组,判断是不是等差数列,如果是的话就累加次数 。这个做法的时间复杂度是O(N^3),对于Python来说是TLE的,但是C++可以通过。

C++代码如下,时间是64ms:

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        const int N = A.size();
        int res = 0;
        for (int i = 0; i < N - 2; i++) {
            for (int j = i + 1; j < N; j++) {
                if (isArithmetic(A, i, j))
                    res ++;
            }
        }
        return res;
    }
private:
    bool isArithmetic(vector<int>& A, int start, int end) {
        if (end - start < 2) return false;
        for (int i = start; i < end - 1; i++) {
            if (A[i + 1] * 2 != A[i] + A[i + 2])
                return false;
        }
        return true;
    }
};

暴力判断的优化

在上面的做法中,我们对每个子数组都进行了从头到尾的判断。其实如果我们已经知道前面部分不是等差数列以后,后面就不用判断了。同时,我们知道等差数列的所有的相邻数字的差是固定的,因此,我们对于每个起始位置只需要向后进行一遍扫描即可。

C++代码如下,时间复杂度是O(N^2),运行时间是0ms.

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        const int N = A.size();
        int res = 0;
        for (int i = 0; i < N - 2; i++) {
            int d = A[i + 1] - A[i];
            for (int j = i + 1; j < N - 1; j++) {
                if (A[j + 1] - A[j] == d) 
                    res ++;
                else
                    break;
            }
        }
        return res;
    }
};

递归解法

从上面的思路中,我们已经逐渐的抽象出一个模型了:固定起点,判断后面的等差数列有多少个。我们可以使用递归,起点固定是0,终点是变化的end,每次从后向前遍历,找出等差数列的长度是多少,加到结果里面。

这个时间复杂度是O(N)。因为递归函数最多被调用N-2次,每次调用的时候没有循环。

class Solution(object):
    def numberOfArithmeticSlices(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        N = len(A)
        self.res = 0
        self.slices(A, N - 1)
        return self.res
    
    def slices(self, A, end):
        if end < 2: return 0
        op = 0
        if A[end] - A[end - 1] == A[end - 1] - A[end - 2]:
            op = 1 + self.slices(A, end - 1)
            self.res += op 
        else:
            self.slices(A, end - 1)
        return op

动态规划

引用自:http://blog.csdn.net/camellhf/article/details/52824234

我的想法是通过扫描一遍数组就能得到结果,所以得先知道如果扫描发现下一个数字能够加入到等差数列中,那么总的数目会有怎样的变化。
因此,我列出了下表:

数组	        等差数列的数目	    与上一数组的等差数列数目比较
1 2 3	            1	                    1 - 0 = 1
1 2 3 4	            3	                    3 - 1 = 2
1 2 3 4 5	        6	                    6 - 3 = 3
1 2 3 4 5 6	        10	                    10 - 6 = 4
1 2 3 4 5 6 7	    15	                    15 - 10 = 5

可以看出等差数列的数目是二阶等差数列。

观察就能发现两个等差数列数目之差(表格第三列)就是[1,2, 3, 4, 5……]这个序列,因此每次增加一个等差数列的元素,总的等差数列的数目就会增加[1,2, 3, 4, 5……]中对应的数值。

按照这一点,在代码实现时就设置一个变量addend,表示增加的数目,它对应着[1,2, 3, 4, 5……]这个序列,如果下一个数组元素能够加入到等差数列中,addend就自增1,然后总的数目就增加addend。如果下一个数组元素不能加入到等差数列中,addend就重置为0。这样通过一个循环就能获得结果。

为什么要把addend设置成0呢?因为题目要求能构成等差数列的子串的数目。设置成0开始计算后面的子串能够成等差数列的数目了。

时间复杂度是O(N)。

class Solution(object):
    def numberOfArithmeticSlices(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        count = 0
        addend = 0
        for i in xrange(len(A) - 2):
            if A[i + 1] - A[i] == A[i + 2] - A[i + 1]:
                addend += 1
                count += addend
            else:
                addend = 0
        return count

二刷,上面的做法其实就是DP的空间复杂度优化,真正的DP方法如下:

class Solution(object):
    def numberOfArithmeticSlices(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        N = len(A)
        dp = [0] * N
        for i in range(1, N - 1):
            if A[i - 1] + A[i + 1] == A[i] * 2:
                dp[i] = dp[i - 1] + 1
        return sum(dp)

日期

2018 年 2 月 28 日
2018 年 12 月 10 日 —— 又是周一!

你可能感兴趣的:(LeetCode,算法)