作者: 负雪明烛
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.

题目大意

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

解题方法

本题要我们求数组中有多少个等差数列。

本题解分成了 4 个部分:暴力 => 双指针 => 递归 => 动态规划

暴力

最容易想到的就是暴力解法:判断所有的子数组是不是等差数列,如果是的话就累加次数。

C++ 代码如下:

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;
}
};
  • 时间复杂度:

    O

    (

    N

    3

    )

    O(N ^ 3)

    O(N3),遍历所有的子数组,需要有两重循环,时间复杂度是

    O

    (

    N

    2

    )

    O(N^2)

    O(N2);判断每个子数组是不是等差数列,时间复杂度是

    O

    (

    N

    )

    O(N)

    O(N);所以总的时间复杂度是

    O

    (

    N

    3

    )

    O(N ^ 3)

    O(N3) 。

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1)。

双指针

在上面的暴力解法中,我们对每个子数组都进行了是否为等差数列的判断。

  • 其实,如果我们已经知道一个子数组的前面部分不是等差数列以后,那么后面部分就不用判断了。
  • 同时,我们知道等差数列的所有的相邻数字的差是固定的。

因此,对于每个起始位置,我们只需要向后进行一遍扫描,直到不再构成等差数列为止,此时已经没有必要再向后扫描。

这个思路其实就是双指针(滑动窗口) 的解法。

C++ 代码如下,

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;
}
};
  • 时间复杂度:

    O

    (

    N

    2

    )

    O(N^2)

    O(N2);

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1)。

递归

从上面的思路中,我们已经逐渐的抽象出一个思路了:固定起点,判断后面的等差数列有多少个

类似的思路,我们可以构造出「自顶向下」的递归解法:定义递归函数 slices(A, end)的含义是区间 A[0, end] 中,end 作为终点的,等差数列的个数。

A[0, end]内的end作为终点的等差数列的个数,相当于在 A[0, end - 1]的基础上,增加了 A[end]

有两种情况:

  1. A[end] - A[end - 1] == A[end - 1] - A[end - 2]时,说明增加的A[end]能和前面构成等差数列,那么 slices(A, end) = slices(A, end - 1) + 1
  2. A[end] - A[end - 1] != A[end - 1] - A[end - 2]时, 说明增加的 A[end]不能和前面构成等差数列,所以slices(A, end) = 0;

最后,我们要求的是整个数组中的等差数列的数目,所以需要把

0

<

=

e

n

d

<

=

l

e

n

(

A

1

)

0 <= end <= len(A - 1)

0<=end<=len(A−1) 的所有递归函数的结果累加起来。

class Solution(object):
def numberOfArithmeticSlices(self, A):
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
  • 时间复杂度:

    O

    (

    N

    2

    )

    O(N^2)

    O(N2)。因为递归函数最多被调用 N 次,每次调用的时候都需要向前遍历一次。

  • 空间复杂度:

    O

    (

    N

    )

    O(N)

    O(N),递归栈的最大深度是 N。

动态规划

上面的递归的解法,是「自顶向下」的思路。如果转成「自底向上」的思路,就变成了动态规划。

类似于递归解法,我们定义

d

p

[

i

]

dp[i]

dp[i] 是以

A

[

i

]

A[i]

A[i] 为终点的等差数列的个数

类似于上面的递归思路,有两种情况:

  1. A[i] - A[i - 1] == A[i - 1] - A[i - 2]时,说明增加的A[i]能和前面构成等差数列,那么 dp[i] = dp[i - 1] + 1
  2. A[i] - A[i - 1] != A[i - 1] - A[i - 2]时, 说明增加的 A[i]不能和前面构成等差数列,所以dp[i] = 0;

动态规划的初始状态:

d

p

[

0

]

=

0

,

d

p

[

1

]

=

0

dp[0] = 0, dp[1] = 0

dp[0]=0,dp[1]=0。

最后,我们要求的是整个数组中的等差数列的数目,所以需要把

0

<

=

i

<

=

l

e

n

(

A

1

)

0 <= i <= len(A - 1)

0<=i<=len(A−1) 的所有

d

p

[

i

]

dp[i]

dp[i] 的结果累加起来。

class Solution(object):
def numberOfArithmeticSlices(self, A):
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)
  • 时间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

  • 空间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

由于 dp[i] 只和 dp[i - 1] 有关,所以可以进行状态压缩,只用一个变量 k 来表示以

A

[

i

]

A[i]

A[i] 为终点的等差数列的个数

计算的方式仍然不变。

class Solution(object):
def numberOfArithmeticSlices(self, A):
count = 0
k = 0
for i in xrange(len(A) - 2):
if A[i + 1] - A[i] == A[i + 2] - A[i + 1]:
k += 1
count += k
else:
k = 0
return count
  • 时间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1);

日期

2018 年 2 月 28 日
2018 年 12 月 10 日 —— 又是周一!
2021 年 8 月 10 日 —— 昨天开车去了雁栖湖

【LeetCode】413. Arithmetic Slices 等差数列划分的更多相关文章

  1. 413 Arithmetic Slices 等差数列划分

    如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列.例如,以下数列为等差数列:1, 3, 5, 7, 97, 7, 7, 73, -1, -5, -9以下数列不是等差数列. ...

  2. LeetCode 413 Arithmetic Slices详解

    这个开始自己做的动态规划复杂度达到了O(n), 是用的是2维的矩阵来存前面的数据,复杂度太高了, 虽然好理解,但是没效率,后面看这个博客发现没有动态规划做了这个题 也是比较厉害. 转载地址: http ...

  3. LN : leetcode 413 Arithmetic Slices

    lc 413 Arithmetic Slices 413 Arithmetic Slices A sequence of number is called arithmetic if it consi ...

  4. [LeetCode]413 Arithmetic Slices

    A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...

  5. LeetCode - 413. Arithmetic Slices - 含中文题意解释 - O(n) - ( C++ ) - 解题报告

    1.题目大意 A sequence of number is called arithmetic if it consists of at least three elements and if th ...

  6. Week 8 - 338.Counting Bits & 413. Arithmetic Slices

    338.Counting Bits - Medium Given a non negative integer number num. For every numbers i in the range ...

  7. Leetcode 413. Arithmetic Slice 算术序列切片(动态规划,暴力)

    Leetcode 413. Arithmetic Slice 算术序列切片(动态规划,暴力) 题目描述 如果一个数组1.至少三个元素2.两两之间差值相同,那么这个数组就是算术序列 比如下面的数组都是算 ...

  8. 【Leetcode】413. Arithmetic Slices

    Description A sequence of number is called arithmetic if it consists of at least three elements and ...

  9. 413. Arithmetic Slices

    /**************************Sorry. We do not have enough accepted submissions.*********************** ...

随机推荐

  1. pyquery解析库的介绍和使用

    ### pyquery的介绍和使用 ## 测试文本 text = ''' <html><head><title>there is money</title&g ...

  2. mysql—MySQL数据库中10位时间戳转换为标准时间后,如何对标准时间进行加减X天处理

    在这篇的缘由:问题:"FROM_UNIXTIME(timeline,'%Y-%m')"的结果(2020-06)做月份增加1月或者减少1月的计算处理,想着直接在结果上+1但是,结果为 ...

  3. lsof之列出已打开的文件

    lsof命令常用解析 Linux中常用 lsof 来查看文件调用进程等相关信息,也可用来查看活跃的进程信息和端口监听进程信息等 1. lsof 命令介绍 NAME lsof - list open f ...

  4. C++类成员初始化列表的构造顺序

    看下面代码, 输出结果是多少呢? class A{ public: A(int k) : j(k), i(j) { } void show() { cout << this->i & ...

  5. 质量体系建设之路---可视化的MockServer

    一. 背景 福禄网络作为一家数字权益商品及服务提供商,覆盖了我们衣食住行的各种生活场景的权益内容,对接了如支付宝.京东.银行APP各种渠道,如何能够快速的响应渠道需求,提供稳定的接口服务,这就要求我们 ...

  6. Splay(伸展树)/HDU6873

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6873 题目大意 给定一组 \(n\) 列的方块,每列方块数 \(b_i\) ,现有 \(q\) 次操作 ...

  7. C++11的auto自动推导类型

    auto是C++11的类型推导关键字,很强大 例程看一下它的用法 #include<vector> #include<algorithm> #include<functi ...

  8. vmware使用nat连接配置

    一.首先查看自己的虚拟机服务有没有开启,选择电脑里面的服务查看: 1.计算机点击右键选择管理  2.进入管理选择VM开头的服务如果没有开启的话就右键开启  二.虚拟机服务开启后就查看本地网络虚拟机的网 ...

  9. ORACLE DBMS_ROWID包详解

    这个包在11gR2中有11个函数或存储: 1. 根据给定参数返回一个rowid --根据给定参数返回一个rowid FUNCTION rowid_create(rowid_type IN NUMBER ...

  10. 关于form表单提交ajaxForm和ajaxSubmit的用法与区别

    前几天在学习form表单提交时看到这两种方法,这两种方法都是实现form的ajax提交的方法,看了很多资料还是不太明白其用法和区别,最后直接自己写demo,很快就理解,所以说实操是学习的最快捷直接的途 ...