【动归】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] dp[i] 表示区间 [ 0 , i ] [0, i] [0,i] 内最长等差子序列。
  • 思考状态转移方程
    • 满足 d p [ i ] − d p [ j ] = d i f f dp[i] - dp[j] = diff 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) dp[i]=max(dp[i],dp[j]+1)
  • 思考初始化
    • d p [ 0... i ] = 1 dp[0...i] = 1 dp[0...i]=1
  • 思考输出 d p [ N − 1 ] dp[N-1] dp[N1]

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

  • if (arr[i] - arr[j] == difference || arr[j] - arr[i] == difference) 怎么样? 3 − ( − 2 ) = = 5 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(N2)
  • 空间复杂度: O ( N ) 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 map[n]=map[ndiff]+1
class Solution {
    public int longestSubsequence(int[] a, int diff) {
        Map<Integer, Integer> f = new HashMap<>();
        int max = 1;
        
        for (int x : a) {
            f.put(x, f.getOrDefault(x-diff, 0)+1);
            max = Math.max(max, f.get(x));
        }
        return max;
    }
}

Cpp 代码比较直观

for (int x : a) {
	int v = ++dp[x - diff];
    max = Math.max(max, v);
}

复杂度分析

  • 时间复杂度: O ( N ) O(N) 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] arr[i]arr[j] d p [ i ] = d p [ j ] + 1 dp[i] = dp[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;
}

你可能感兴趣的:(●,动态规划)