上面的这两个都可以直接用dp来写,但是dp的话复杂度是 n 2 n^2 n2,复杂度还是太大了
先从最长上升子序列说起
二分+贪心
我们可以维护一个到 a [ i ] a[i] a[i]的最长上升子序列,每次遇到一个新的数 x x x 就在刚刚的序列里面二分查找第一个大于等于 x x x 的位置,这里直接二分就行,如果序列的最大一个元素还是小于 x x x,那么我们的x就直接放到序列的最后面
[ 2 , 4 , 7 ] [2 , 4, 7] [2,4,7] 当前 x = 8, 那么8就可以直接插入序列的末尾就可以维护一个更长的上升序列
[ 2 , 4 , 7 ] [2 , 4, 7] [2,4,7] 当前 x = 5, 那么5应该插入到 7 的位置,这样可以维护一个长度相等,但是一个更优的序列
代码如下:
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
g = []
for x in nums:
j = bisect_left(g,x)
if j == len(g):
g.append(x)
else:
g[j] = x
return len(g)
如果是最长非递减子序列呢,这个就要求我们维护的序列中可以存在相同的元素
改进也很简单
# 上面的代码只用修改 bisect_left
# 改成
j = bisect_right(g,x)
万能法:线段树或树状数组
我们可以直接定义 d p [ i ] dp[i] dp[i] 为以 a [ i ] a[i] a[i] 结尾的序列的最大长度,那么应该怎么转换呢
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
)
+
1
,
j
<
i
a
[
j
]
<
a
[
i
]
dp[i] = max(dp[j])+1,j<i a[j]<a[i]
dp[i]=max(dp[j])+1,j<ia[j]<a[i]
我们怎么找到所有索引在
i
i
i之前,且
a
[
j
]
<
a
[
i
]
a[j] < a[i]
a[j]<a[i] 的
d
p
[
j
]
dp[j]
dp[j] 最大值呢
我们用到权值线段树,不用数组 a [ i ] a[i] a[i] 的下标索引 i i i 作为线段树的插入索引,而是用 a [ i ] a[i] a[i]的值作为索引, d p [ i ] dp[i] dp[i] 作为线段树中我们要维护的值,我们每次只要查询 [ 1 , a [ i ] ] [1,a[i] ] [1,a[i]] 之间的最大值就可以得到满足的最大的 d p [ j ] dp[j] dp[j],(注意,最长上升子序列是 [ 1 , a [ i ] − 1 ] [1,a[i]-1] [1,a[i]−1],最长非递减子序列是 [ 1 , a [ i ] ] [1,a[i]] [1,a[i]]
注意一下update函数的更新细节
if l==r:
self.t[o] = max(self.t[o],x) # 可能会重复更新