这道题目是300. 最长递增子序列的进阶版。
解决代码如下:
from bisect import bisect_left
class Solution(object):
def findNumberOfLIS(self, nums):
dp = [nums[0]]
dp1 = [{nums[0] : 1}]
for i in nums[1 : ]:
if i > dp[-1]:
dp.append(i)
dp1.append({i : 0})
for key,value in dp1[-2].items():
if i > key:
dp1[-1][i] += value
else:
pos = bisect_left(dp, i)
dp[pos] = i
if i not in dp1[pos]:
dp1[pos][i] = 0
if pos == 0:
dp1[0][i] += 1
else:
for key, value in dp1[pos - 1].items():
if i > key:
dp1[pos][i] += value
res = 0
for value in dp1[-1].values():
res += value
return res
原理:
维护dp和dp1数组
dp数组索引 i 处记录的是当前构成长度为 i+1 的递增子序列的最小末尾数
dp1和dp很类似,但是dp1索引 i 处记录的是应该字典,字典中记录的是当前所有长度为 i+1 的子序列,当它们末尾数为key时,有value种构成的方式(key必须构成尽量长的子序列,例如以7为末尾数时,最长能构成长度为3的子序列)
对nums数组遍历
比如 nums = [10,9,2,5,3,7,21,18]
当遍历到nums[6] = 21时:
因为构成长度为1的子序列有[10],[9],[2],[5],[3],[7],最小的末尾数是2
构成长度为2的子序列有[2,5],[2,3],[2,7],[3,7],[5,7],最小的末尾数是3
构成长度为3的子序列有[2,5,7]和[2,3,7],最小的末尾数是7
所以此时dp = [2,3,7]
因为dp是一个单调递增的数组,当遍历到nums[n](记为num)时,如果num > dp[-1],就在dp末尾插入num,否则,用bisect模块中的bisect_left函数,定位num应该在dp中插入的位置pos,然后更新
注意不是把num插入到dp[pos]位置,而是将dp[pos]更新为num
例如当遍历到nums[7] = 18时,此时dp = [2,3,7,21],定位18在dp中的位置,pos = 3,所以dp[3] = 18,dp = [2,3,7,18]
接下来是如何维护dp1
当遍历到nums[5] = 7(记为num)时:
此时dp1 = [{10:1, 9:1, 2:1}, {5:1, 3:1}]
由pos可知,当num为7时,最长的子序列长度变成了3,所以要在dp1末尾插入一个新的字典,这个字典新增key = 7,value = 当前长度为2的子序列,且末尾数小于7(这里有2和5)的key所对应的value之和 1+1=2
同理当遍历到nums[7] = 18 时:
此时dp1 = [{10:1, 9:1, 2:1}, {5:1, 3:1}, {7:2}, {21:2}]
由pos可知,18应该作为key添加到子序列长度为4的字典里面,即dp1[3],value等于当前子序列长度为3,且末尾数小于18(只有dp1[3][7])的key所对应的value之和 2
当遍历完nums数组以后,dp1[-1]中的全部value加起来,就是所要求的最长递增子序列的个数