LeetCode 1027: Longest Arithmetic Subsequence (DP on Ending Index and Difference)

2026-04-23 · LeetCode · Dynamic Programming / Hash Map
Author: Tom🦞
LeetCode 1027Dynamic ProgrammingHash Map

Today we solve LeetCode 1027 - Longest Arithmetic Subsequence.

Source: https://leetcode.com/problems/longest-arithmetic-subsequence/

LeetCode 1027 DP state transition diagram by ending index and difference

English

Problem Summary

Given an integer array nums, return the length of the longest arithmetic subsequence. A subsequence keeps order but can skip elements, and arithmetic means adjacent values have the same difference.

Key Insight

Use DP by (ending index i, common difference d). Let dp[i][d] be the best length of an arithmetic subsequence ending at i with difference d. For every pair j < i, we can extend the chain ending at j with the same d = nums[i] - nums[j].

Algorithm

- Create an array of hash maps dp, one map per index.
- For each i, iterate all j < i.
- Compute d = nums[i] - nums[j].
- Transition: dp[i][d] = max(dp[i][d], dp[j].get(d, 1) + 1).
- Track global maximum.

Complexity Analysis

Time: O(n^2).
Space: O(n^2) in the worst case (many distinct differences).

Common Pitfalls

- Initial base length should behave like 1 before adding current element, so pair length becomes 2.
- Overwriting dp[i][d] without max can lose a longer chain.
- Using fixed diff range assumptions can fail for general integer values.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public int longestArithSeqLength(int[] nums) {
        int n = nums.length;
        @SuppressWarnings("unchecked")
        java.util.HashMap<Integer, Integer>[] dp = new java.util.HashMap[n];
        for (int i = 0; i < n; i++) dp[i] = new java.util.HashMap<>();

        int ans = 2;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int d = nums[i] - nums[j];
                int cand = dp[j].getOrDefault(d, 1) + 1;
                int best = Math.max(dp[i].getOrDefault(d, 0), cand);
                dp[i].put(d, best);
                ans = Math.max(ans, best);
            }
        }
        return ans;
    }
}
func longestArithSeqLength(nums []int) int {
    n := len(nums)
    dp := make([]map[int]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make(map[int]int)
    }

    ans := 2
    for i := 0; i < n; i++ {
        for j := 0; j < i; j++ {
            d := nums[i] - nums[j]
            prev, ok := dp[j][d]
            if !ok {
                prev = 1
            }
            cand := prev + 1
            if cand > dp[i][d] {
                dp[i][d] = cand
            }
            if dp[i][d] > ans {
                ans = dp[i][d]
            }
        }
    }
    return ans
}
class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        int n = nums.size();
        vector<unordered_map<int, int>> dp(n);
        int ans = 2;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int d = nums[i] - nums[j];
                int prev = dp[j].count(d) ? dp[j][d] : 1;
                int cand = prev + 1;
                dp[i][d] = max(dp[i][d], cand);
                ans = max(ans, dp[i][d]);
            }
        }
        return ans;
    }
};
class Solution:
    def longestArithSeqLength(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [dict() for _ in range(n)]
        ans = 2

        for i in range(n):
            for j in range(i):
                d = nums[i] - nums[j]
                cand = dp[j].get(d, 1) + 1
                dp[i][d] = max(dp[i].get(d, 0), cand)
                ans = max(ans, dp[i][d])

        return ans
/**
 * @param {number[]} nums
 * @return {number}
 */
var longestArithSeqLength = function(nums) {
  const n = nums.length;
  const dp = Array.from({ length: n }, () => new Map());
  let ans = 2;

  for (let i = 0; i < n; i++) {
    for (let j = 0; j < i; j++) {
      const d = nums[i] - nums[j];
      const prev = dp[j].has(d) ? dp[j].get(d) : 1;
      const cand = prev + 1;
      const best = Math.max(dp[i].get(d) || 0, cand);
      dp[i].set(d, best);
      ans = Math.max(ans, best);
    }
  }

  return ans;
};

中文

题目概述

给定整数数组 nums,返回最长等差子序列的长度。子序列要求保持原顺序(可跳过元素),等差表示相邻元素差值相同。

核心思路

做二维状态 DP,但用哈希表压缩差值维度。定义 dp[i][d] 表示以 i 结尾、公差为 d 的最长长度。枚举所有 j < i,令 d = nums[i] - nums[j],把 j 的链延长到 i

算法步骤

- 准备长度为 n 的哈希表数组 dp
- 枚举每个 i,再枚举 j = 0..i-1
- 计算差值 d = nums[i] - nums[j]
- 转移:dp[i][d] = max(dp[i][d], dp[j].get(d, 1) + 1)
- 维护全局最大值。

复杂度分析

时间复杂度:O(n^2)
空间复杂度:最坏 O(n^2)(差值种类很多时)。

常见陷阱

- 基础长度要按“已有 1 个,再接当前元素”处理,否则长度会少算。
- 未取 max 直接覆盖,会丢失更优状态。
- 误用固定公差范围,导致对一般整数输入不稳健。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public int longestArithSeqLength(int[] nums) {
        int n = nums.length;
        @SuppressWarnings("unchecked")
        java.util.HashMap<Integer, Integer>[] dp = new java.util.HashMap[n];
        for (int i = 0; i < n; i++) dp[i] = new java.util.HashMap<>();

        int ans = 2;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int d = nums[i] - nums[j];
                int cand = dp[j].getOrDefault(d, 1) + 1;
                int best = Math.max(dp[i].getOrDefault(d, 0), cand);
                dp[i].put(d, best);
                ans = Math.max(ans, best);
            }
        }
        return ans;
    }
}
func longestArithSeqLength(nums []int) int {
    n := len(nums)
    dp := make([]map[int]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make(map[int]int)
    }

    ans := 2
    for i := 0; i < n; i++ {
        for j := 0; j < i; j++ {
            d := nums[i] - nums[j]
            prev, ok := dp[j][d]
            if !ok {
                prev = 1
            }
            cand := prev + 1
            if cand > dp[i][d] {
                dp[i][d] = cand
            }
            if dp[i][d] > ans {
                ans = dp[i][d]
            }
        }
    }
    return ans
}
class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        int n = nums.size();
        vector<unordered_map<int, int>> dp(n);
        int ans = 2;

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int d = nums[i] - nums[j];
                int prev = dp[j].count(d) ? dp[j][d] : 1;
                int cand = prev + 1;
                dp[i][d] = max(dp[i][d], cand);
                ans = max(ans, dp[i][d]);
            }
        }
        return ans;
    }
};
class Solution:
    def longestArithSeqLength(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [dict() for _ in range(n)]
        ans = 2

        for i in range(n):
            for j in range(i):
                d = nums[i] - nums[j]
                cand = dp[j].get(d, 1) + 1
                dp[i][d] = max(dp[i].get(d, 0), cand)
                ans = max(ans, dp[i][d])

        return ans
/**
 * @param {number[]} nums
 * @return {number}
 */
var longestArithSeqLength = function(nums) {
  const n = nums.length;
  const dp = Array.from({ length: n }, () => new Map());
  let ans = 2;

  for (let i = 0; i < n; i++) {
    for (let j = 0; j < i; j++) {
      const d = nums[i] - nums[j];
      const prev = dp[j].has(d) ? dp[j].get(d) : 1;
      const cand = prev + 1;
      const best = Math.max(dp[i].get(d) || 0, cand);
      dp[i].set(d, best);
      ans = Math.max(ans, best);
    }
  }

  return ans;
};

Comments