最长递增子序列的个数 动态规划
给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。
示例 1:
输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
思路:
这道题和 最长递增子序列 同源,但是在求长度的基础上加了求个数的要求。其实两道题的解决方法都是使用动态规划 ,且只有状态转移的地方需要做一些修改。如果只求最长递增子序列的长度,dp的状态转移方程非常好理解,但求最长递增子序列的个数的话状态转移方程理解起来会难一点。
首先我们还是需要同样用dp数组,并且是同样的状态转移方程来更新计算出以i结尾的最长子序列的长度,用dp[i]表示,这个方法和这篇里写的一样,后面代码其实也就是在这篇的解决办法上加了点操作。
具体加的操作是额外的一个count数组,count[i]表示以i位置结尾的最长递增子序列的个数。我们开启两层循环去遍历数组,外层用i,内层用j,j<i, 为了得到递增子序列,我们只考虑nums[i]>nums[j]时的情况,此时我们判断dp[i]和dp[j]+1的关系:
1.如果dp[i]<dp[j]+1,就说明i当前的位置可以被当作新的更大值,dp[i]可以被更新到更大,我们去更新dp;同时count[i]等于count[j],因为不管以j结尾的最长递增子序列的个数是多少个,再多算上它i还是多少个。(此时的count[i]只是初始化结束,后续还要进行下面的加等于操作。)
2.如果dp[i]==dp[j]+1,此时说明dp[i]已经被更新到了最大值,此时不需要去更新dp;但要让count[i]加等于count[j],因为此时以j为结尾的所有最长递增子序列又可以去加上i组成新的。
代码:
class Solution(object):
def findNumberOfLIS(self, nums):
lenth=len(nums)
dp=[1]*lenth #dp[i] 以i结尾的最长子序列的长度
count=[1]*lenth #count[i] 以i结尾的最长子序列的个数
for i in range(1,lenth):#两层循环遍历dp数组
for j in range(i):
if nums[i]>nums[j]:#只有当nums[i]>nums[j]才进行dp的更新和判断
# 如果dp[j] + 1 > dp[i],说明最长递增子序列的长度增加了
if dp[i]<dp[j]+1:
#dp[i]更新为dp[j] + 1,重新初始化 count[i] = count[j]
dp[i]=dp[j]+1
count[i]=count[j]
#如果dp[j]+1==dp[i]说明dp[i]已经更新到当前最大但又出现了一样的dp[j]
elif dp[i]==dp[j]+1:
#直接增加 count[i] += count[j]
count[i]+=count[j]
maxx=max(dp)#先找到最大长度
res = 0
for i,d in enumerate(dp):
if d==maxx:#找到所有最大长度的位置
res+=count[i]#把这个位置对应的个数加到结果里去
return res
小结:
我感觉这道题应该算是困难题了(虽然写的是中等难度),我确实觉得这个dp思路确实不管是写起来还是理解起来都比较困难。可以尝试只看我的代码,或许就直接懂了。
最后再对思路做个小补充:我们不能一开始就初始化count[i]=0然后两种情况都用count[i]加等于count[j],因为这样会把不是最长递增子序列的个数都加进来。第一种情况不用count[i]加等于count[j]是为了一旦遇到更长的最长递增序列,我们就要重新初始化为count[j]。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了