【动归】B014_最长等差子序列(dp(映射表))

一、题目描述

Given an integer array arr and an integer difference, 
return the length of the longest subsequence in arr 
which is an arithmetic sequence such that the difference 
between adjacent elements in the subsequence equals difference.

Input: arr = [1,3,5,7], difference = 1
Output: 1
Explanation: The longest arithmetic subsequence is any single element.

二、题解

方法一:直接 dp

  • 定义状态
    • d p [ i ] dp[i] 表示区间 [ 0 , i ] [0, i] 内最长等差子序列。
  • 思考状态转移方程
    • 满足 d p [ i ] d p [ j ] = d i f f dp[i] - dp[j] = diff 时, d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i] = max(dp[i], dp[j] + 1)
  • 思考初始化
    • d p [ 0... i ] = 1 dp[0...i] = 1
  • 思考输出 d p [ N 1 ] dp[N-1]

你有没有考虑到数组 arr 中存在负数?比如: [ 3 , 4 , 3 , 2 , 4 ] [3, 4,-3,-2,-4] d i f f = 5 diff = -5 时,答案应该是 2 即子序列 [3, -2]。而依照上面的算法根本算不出 2,把条件判断改为:

  • if (arr[i] - arr[j] == difference || arr[j] - arr[i] == difference) 怎么样? 3 ( 2 ) = = 5 3 - (-2) == 5 ,看来也不行。
public int longestSubsequence(int[] arr, int difference) {
  int N = arr.length;
  int[] dp = new int[N];
  Arrays.fill(dp, 1);

  for (int i = 1; i < N; i++)
  for (int j = 0; j < i; j++) {
    if (arr[i] - arr[j] == difference)
      dp[i] = Math.max(dp[i], dp[j] + 1);
  }
  return dp[N-1];
}

复杂度分析

  • 时间复杂度: O ( N 2 ) O(N^2)
  • 空间复杂度: O ( N ) O(N)

方法二:map

使用映射表存储等差数列的元素无需考虑子结构的顺序性,拿出一个数 n,直接查找它的等差数:

  • 如果查找到,则以 n 结尾的最长等差子序列长度 +1;
  • 如果没找到,则以 n 结尾的最长等差子序列长度还是 1。
  • 状态转移不难想到: m a p [ n ] = m a p [ n d i f f ] + 1 map[n] = map[n-diff] + 1
public int longestSubsequence(int[] arr, int difference) {
  HashMap<Integer, Integer> dp = new HashMap<>();
  int LS = 0;
  for (int n : arr) {
    dp.put(n, dp.getOrDefault(n-difference, 0) + 1);
    LS = Math.max(LS, dp.get(n));
  }
  return LS;
}

复杂度分析

  • 时间复杂度: O ( N ) O(N)
  • 空间复杂度: O ( N ) O(N)

偏置数组

-10^4 <= arr[i], difference <= 10^4,由此可得差值 diff 不超过 10000,直接用数组快得很。转移方程还是那样:

  • 如果 a r r [ i ] a r r [ j ] arr[i] - arr[j] d p [ i ] = d p [ j ] + 1 dp[i] = dp[j] + 1
public int longestSubsequence(int[] arr, int difference) {
  int[] dp = new int[20006];
  int len = 0;
  int N = arr.length;
  for (int i = 0; i < N; i++) arr[i] += 10000;  //变为正数

  for (int i : arr) {
    int j = i - difference;
    if (j >= 0 && j <= 20000) dp[i] = dp[j] + 1;
    else                      dp[i] = 1;
    len = Math.max(dp[i], len);
  }
  return len;
}
发布了461 篇原创文章 · 获赞 102 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/104684512
今日推荐