1. 问题描述:
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。例如,以下数列为等差数列:
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。
1, 1, 2, 5, 7
数组 A 包含 N 个数,且索引从 0 开始。该数组子序列将划分为整数序列 (P0, P1, ..., Pk),满足 0 ≤ P0 < P1 < ... < Pk < N。
如果序列 A[P0],A[P1],...,A[Pk-1],A[Pk] 是等差的,那么数组 A 的子序列 (P0,P1,…,PK) 称为等差序列。值得注意的是,这意味着 k ≥ 2。函数要返回数组 A 中所有等差子序列的个数。
输入包含 N 个整数。每个整数都在 -2 ^ 31 和 2 ^ 31-1 之间,另外 0 ≤ N ≤ 1000。保证输出小于 2 ^ 31 - 1。
示例:
输入:[2, 4, 6, 8, 10]
输出:7
解释:
所有的等差子序列为:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence
2. 思路分析:
这道题目属于经典的动态规划题目,主要分为两个步骤:① 状态定义 ② 状态计算,分析题目可以知道我们可以声明一个二维的dp列表,dp[i][j]表示以当前a[i]结尾公差为j的长度大于等于2的等差序列的数目(一般都是以a[i]结合题目考虑一下如何定义),因为第二维是公差而且公差的范围很大所以对于dp列表的第二维我们声明为字典类型,虽然这里定义的是长度为2的等差序列的数目,但是我们在遍历的时候是可以计算到长度大于等于3的等差序列的,在计算的时候累加长度大于等于3的等差序列即可。因为是以a[i]结尾,所以我们在状态计算的时候枚举[0:i],枚举的时候可以计算出j = a[i] - a[k],j就是公差我们在字典中看是否可以找到以a[k]结尾并且公差为j的值,如果存在说明以当前的a[i]结尾的序列可以构成等差序列,当我们字典中找到这个值的时候说明以a[i]结尾的等差序列的长度是大于等于3的,累加到答案然后更新一下dp[i][j]的值即可。
3. 代码如下:
from typing import List
import collections
class Solution:
# 关键是状态定义和状态计算
def numberOfArithmeticSlices(self, a: List[int]) -> int:
n = len(a)
# 因为第二维是公差, 而且公差可能为负数并且有可能特别大所以第二维声明为字典类型
dp = [collections.defaultdict(int) for i in range(n)]
res = 0
for i in range(n):
for k in range(i):
# 公差
j = a[i] - a[k]
t = 0
# 字典中找到了这个值表示以当前的a[i]结尾的等差序列的长度就是大于等于3的累加到答案中即可
if dp[k][j] > 0:
t = dp[k][j]
res += t
# 更新答案
dp[i][j] += t + 1
return res
from typing import List
class Solution:
def numberOfArithmeticSlices(self, a: List[int]):
n = len(a)
dp = [dict() for i in range(n + 10)]
res = 0
for i in range(n):
for k in range(i):
# j为公差
j = a[i] - a[k]
t = 0
if j in dp[k]:
t = dp[k][j]
res += t
if j not in dp[i]: dp[i][j] = 0
dp[i][j] += t + 1
return res