数组/字符串-子序列
特点:
- 要保持相对顺序
- 不一定相邻
难点:
- 不确定序列从哪里开始 ( 后缀思考,固定起点, 向后遍历所有 )
- 不确定元素之间的间隔 ( 用题目性质来确定 )
- 不确定序列在哪里结束 ( 前缀思考,固定终点, 向前遍历所有 )
所以就只能以遍历到当前状态来考虑,多用DP来解决
需要考虑的问题:
- 在跨元素的情况下怎么保持关系?( 用题目性质来保持 )
题目
满足某种性质的子序列
这些性质将子序列的元素连接起来,在定义子问题的时候需要考虑组成这些性质的要素。
873. Length of Longest Fibonacci Subsequence
初始思路:
dp[i]: 以i为结尾的最长Fibonacci子序列个数
在跨元素的情况下怎么保持关系?
dp[i]和dp[i-1]之间的关系?
思路纠正:
dp[i]: 以i为结尾的最长Fibonacci子序列个数
这道题虽然是一个数组的问题,但是只考虑以i结尾的情况还不足以定义一个Fib序列。Fib序列至少需要两个元素才能构成,所以将子问题定义成以(i,j)结尾的情况。
子问题定义:dp[i][j]以i,j结尾的Fib子序列的最长的长度 (前缀思考方式)
在跨元素的情况下怎么保持关系?
子问题之间的联系: 根据Fib序列的定义,用A[j]-A[i]将另一个构成Fib序列的X找出来。但不确定这个X是否在原数组中,
- 如果原数组中存在
X,而且它的下标k在i之前andk没有越界(k>=0),那么(i,j)Fib序列就向前扩展到了k,增加了一个长度 - 如果它不在,那么(i,j)构成的Fib序列只能是2
为了便于查询下标k,需要用一个map来保存Value->Key的映射。
怎么保证不漏掉序列?
j每往后走一次,为了不漏掉每一个扩充序列的可能,都要用i向前遍历一遍。
|
|
最后的结果在哪里?
虽然dp[i][j]记录所有可能Fib序列长度,但结果不一定就是dp[n-2][n-1],而应该是dp数组中最大的数: max(dp)
代码实现:
|
|
1027. Longest Arithmetic Sequence
初始思路:
dp[i]: 以i为结尾的最长Arithmetic子序列个数
在跨元素的情况下怎么保持关系?
前缀
dp[i]和dp[i-1]之间的关系?
思路纠正:
子问题定义:
构成等差数列的要素有:一个元素a和公差d
需要记录以A[i]结尾的所有可能的公差的等差数列,最长的的长度
|
|
dp中第一个int表示以A[i]结尾中的i,然后这些序列中的公差d是不确定的,因为任何前后两个元素都可以形成一个潜在的等差数列,嵌套的map<int, int>中第一个int表示公差d,第二个int表示最多到下标i形成的等差数列的最长的长度。
在跨元素的情况下怎么保持关系?
子问题之间的联系:
当考察到下标为i的元素时,需要遍历i之前的每一个元素j。因为j之前的每一个元素A[j]都有可能与A[i]形成等差数列。
|
|
根据等差数列的定义,求出A[i] - A[j]的公差d,然后查看到j为止,公差为d的等差数列的个数
- 如果之前有值,那么因为
A[i]的存在,扩展了这个等差数列,所以长度加1 - 如果之前没有值,那么
A[j] A[i]是第一个以d为公差的等差数列,A[j][d] = 2
选最大的A[i][j],继续下一个i
最后的结果在哪里?
dp最大元素是最终的结果,因为以最后一个元素截止的等差数列不一定最长
代码实现:
|
|
334. Increasing Triplet Subsequence 固定长度的递增子序列
子问题定义:
memo[k]以k结尾的递增子序列的最长度
子问题之间的联系:
如果当前元素A[i]大于之前的…
是不是还需要保存一个i-1之前最大的元素 ?
要不然怎么知道是是否扩充了序列,还是开启了一个新序列?
|
|
300. Longest Increasing Subsequence
|
|
T: O(NlogN)
S: O(N)
376. Wiggle Subsequence (未完待续)
dual status 两个dp状态
dp[i]: 以i为结尾的最长wiggle子序列个数
在跨元素的情况下怎么保持关系?
递推关系?
|
|
需要一个变量来保存之前的大小关系吗? 需要