1218. 最长定差子序列
考点
- 线性DP
思路
一、问题描述
给定一个整数数组 \[
\texttt{arr} = [a_1, a_2, \dots, a_n]
\] 以及一个整数差值 \[
d = \texttt{difference}
\] 要求从 arr
中选取一个子序列(不要求连续),使得该子序列中任意相邻两个元素满足:
\[
a_{i_{k+1}} - a_{i_k} = d
\] 目标是求这样的子序列的最大长度。
二、问题建模
2.1 子序列的结构特征
对于一个满足条件的子序列: \[ x_1, x_2, \dots, x_k \] 必然有: \[ x_{j+1} = x_j + d \] 因此,整个子序列的值是严格按固定差值递推的。 这意味着:
一个以数值 \(x\) 结尾的最优子序列,其前一个元素一定是 \(x - d\)。
2.2 动态规划状态定义
定义状态: \[ f(x) = \text{以数值 } x \text{ 作为结尾的最长定差子序列长度} \] 注意,这里的状态是按“数值”而非“下标”定义的,这是本题的关键建模点。
三、状态转移方程
3.1 转移推导
如果当前处理到的数组元素为 \(x\),那么:
- 若此前存在以 \(x - d\) 结尾的子序列,其长度为 \(f(x - d)\)
- 则可以将 \(x\) 接在该子序列后面
因此状态转移为: \[ f(x) = f(x - d) + 1 \] 如果此前从未出现过 \(x - d\),则视为: \[ f(x - d) = 0 \Rightarrow f(x) = 1 \]
3.2 初始化
在算法开始时: \[ f(x) = 0 \quad \forall x \] 表示尚未形成任何子序列。
3.3 答案提取
最终答案为所有状态中的最大值: \[ \max_x f(x) \]
四、值域优化与数组化实现
4.1 值域分析
根据题目约束: \[ -10^4 \le arr[i],\ difference \le 10^4 \] 因此所有可能访问到的数值: \[ x,\ x - d \in [-2\times10^4,\ 2\times10^4] \] 值域大小是常数级别的。
4.2 平移映射
为了使用数组代替哈希表,引入一个偏移量: \[ \texttt{OFFSET} = 20000 \] 将数值 \(x\) 映射为数组下标: \[ \text{index}(x) = x + \texttt{OFFSET} \] 这样即可保证所有访问都在合法数组范围内。
五、算法流程总结
使用数组
f[]记录状态 \(f(x)\)顺序遍历数组
arr对每个元素 \(x\),执行状态转移: \[ f(x) \leftarrow f(x - d) + 1 \]
维护全局最大值作为答案
六、时间与空间复杂度分析
时间复杂度: \[ O(n) \] 其中 \(n\) 为数组长度
空间复杂度: \[ O(1) \] 值域大小固定(约 \(4\times10^4\)),与输入规模无关
七、AC代码
1 | class Solution { |