446 等差数列划分 II - 子序列(动态规划-递推)

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值