首页 > 其他分享 >LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现N*K时间复杂度

LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现N*K时间复杂度

时间:2024-09-07 23:53:21浏览次数:14  
标签:好子 nums int max 复杂度 lastMax II res dp

3177. 求出最长好子序列 II

题目链接

题目描述

给你一个整数数组 nums 和一个非负整数k 。如果一个整数序列 seq 满足在下标范围 [0, seq.length - 2] 中 最多只有 k 个下标i满足 seq[i] != seq[i + 1] ,那么我们称这个整数序列为好序列。请你返回 nums中好子序列的最长长度。

实例1:

输入:nums = [1,2,1,1,3], k = 2
输出:2
解释:最长的好子序列是 [1,2,1,1] 。

实例2:

输入:nums = [1,2,3,4,5,1], k = 0
输出:2
解释:最长好子序列为 [1,1] 。

题目解析

这道题目是求出最长好子序列 I的升级版,对时间复杂度有了更高的要求。我们在上一篇题解中,给出了时间复杂度为N^2*K的解法。这次需要将时间复杂度降低到N*K

解题思路

这道题目和求出最长好子序列 I的解法类似,也是使用动态规划。

我们同样定义定义dp[i][j]表示以nums[i]结尾,最多有j个下标i 满足seq[i] != seq[i + 1]的子序列的长度。其中,0<=j<=k。

而在上一篇题解中,我们使用了三重循环,来解决问题。

而这次,我们考虑去掉第三重循环。

			for cur := 0; cur < i; cur++ {
				if nums[i] == nums[cur] {
					dp[i][j]=max(dp[i][j],dp[cur][j]+1)
				}else{
                    if(j-1>=0){
                       dp[i][j]=max(dp[i][j],dp[cur][j-1]+1)
                    }
                }
			}

我们看到,循环中只需考虑两种情况

  • 数字i之前有数字和nums[i]相同
  • 数字i之前有数字和nums[i]不同,且j大于0

因此我们使用哈希表lastPos := make(map[int]int) 用于记录和nums[i]相同的数字最后出现的位置。
lastMax := make([]int, k+1) 用于记录不同列的当前最大取值,即dp[cur][j-1]的最大值,其中0 <=cur<i

  • 数字i之前有数字和nums[i]相同,则dp[i][j]=max(dp[i][j],dp[lastPos[nums[i]]][j]+1)
  • 数字i之前有数字和nums[i]不同,且j大于0,则dp[i][j]=max(dp[i][j],lastMax[j-1]+1)

代码实现

Go版本:

func maximumLength(nums []int, k int) int {
	n := len(nums)
	dp := make([][]int, n)
	for i := range dp {
		dp[i] = make([]int, k+1)
	}
	res := 0
	lastPos := make(map[int]int) // 用于记录每个数字的最后出现位置
	lastMax := make([]int, k+1)  // 用于记录第 j 列的最大值
	lastNew := make([]int, k+1)  // 用于临时保存本轮计算中的最大值

	for i := 0; i < n; i++ {
		dp[i][0] = 1
		// 在每次外循环开始时,重置 lastNew 为 lastMax 的当前状态
		copy(lastNew, lastMax)

		for j := 0; j <= k && j <= i; j++ {
			// 如果数字之前出现过,更新 dp[i][j] 的值
			if pos, found := lastPos[nums[i]]; found {
				dp[i][j] = max(dp[i][j], dp[pos][j]+1)
			}
			// 如果允许更多的 k,考虑使用 lastMax[j-1]
			if j > 0 {
				dp[i][j] = max(dp[i][j], lastMax[j-1]+1)
			}
			// 更新 lastNew 和最终结果
			lastNew[j] = max(lastNew[j], dp[i][j])
			res = max(res, dp[i][j])
		}

		// 外循环结束时,将 lastMax 更新为本轮的 lastNew
		copy(lastMax, lastNew)
		// 更新当前数字最后一次出现的位置
		lastPos[nums[i]] = i
	}

	return res
}

C++版本:

class Solution {
public:
    int maximumLength(vector<int>& nums, int k) {
        int n=nums.size();
        vector<vector<int>> dp(n,vector<int>(k+1,0));
        int res=0;
        vector<int> lastMax(k+1,0);
        vector<int> lastTemp(k+1, 0);
        unordered_map<int,int> lastPos;
        for(int i=0;i<n;i++){
            dp[i][0]=1;
            for(int j=0;j<=k;j++){
                if(lastPos.count(nums[i])){
                    dp[i][j]=max(dp[i][j],dp[lastPos[nums[i]]][j]+1);
                }
                if(j>0){
                dp[i][j]=max(dp[i][j],lastMax[j-1]+1);
                }
                lastTemp[j]=max(lastTemp[j],dp[i][j]);
                res=max(res,dp[i][j]);
            }
            lastPos[nums[i]]=i;
            lastMax=lastTemp;
        }
        return res;
    }
};

Python版本:

class Solution(object):
    def maximumLength(self, nums, k):
        n = len(nums)
        dp = [[0] * (k + 1) for _ in range(n)]
        res = 0
        last_max = [0] * (k + 1)
        last_temp = [0] * (k + 1)
        last_pos = {}
        
        for i in range(n):
            dp[i][0] = 1
            for j in range(k + 1):
                if nums[i] in last_pos:
                    dp[i][j] = max(dp[i][j], dp[last_pos[nums[i]]][j] + 1)
                if j > 0:
                    dp[i][j] = max(dp[i][j], last_max[j - 1] + 1)
                last_temp[j] = max(last_temp[j], dp[i][j])
                res = max(res, dp[i][j])
            last_pos[nums[i]] = i
            last_max = last_temp[:]
        
        return res

标签:好子,nums,int,max,复杂度,lastMax,II,res,dp
From: https://blog.csdn.net/weixin_60214397/article/details/142006436

相关文章

  • 【Golang】LeetCode面试经典150题:45. 跳跃游戏 II
    题干:给定一个长度为 n 的 0索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i+j] 处:0<=j<=nums[i] i+j<n返回到达 nums[n-1] 的最小跳跃次数......
  • 【Ynoi 2019 模拟赛】Yuno loves sqrt technology III
    LuoguP5048YunolovessqrttechnologyIII题意给定一个长度为\(n\)的序列\(a\)。有\(m\)次询问:查询区间\([l,r]\)中众数的出现次数。强制在线。数据范围与约定\(1\len,m,a_i\le5*10^5\)。题解十年前《蒲公英》的做法,这道题只能拿\(80\)分,因为这道题卡了空......
  • 代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II
    代码随想录刷题day25丨491.递增子序列,46.全排列,47.全排列II1.题目1.1递增子序列题目链接:491.非递减子序列-力扣(LeetCode)视频讲解:回溯算法精讲,树层去重与树枝去重|LeetCode:491.递增子序列_哔哩哔哩_bilibili文档讲解:https://programmercarl.com/0491.%E9%80......
  • 求出最长好子序列
    给你一个整数数组nums和一个非负整数k。如果一个整数序列seq满足在范围下标范围[0,seq.length-2]中存在不超过k个下标i满足seq[i]!=seq[i+1],那么我们称这个整数序列为好序列。请你返回nums中好子序列的最长长度1.动态规划dp[i][j]表示将把i作为序......
  • 触想全新Z系列工控机扩展IIoT应用潜能
    8月31日,触想重磅推出全新Z系列高性能、扩展型工控机——TPC05/06/07-WIPC,提供标准版/双卡槽/四卡槽3款机型选择。作为边缘计算、机器视觉、AI智能和工业应用的理想机型,Z系列工控机支持Intel®第12/13/14代Core™i3/i5/i7/i9处理器,最多搭载4个PCIe/PCI的扩展能力,可外接多种......
  • 触想全新Z系列工控机扩展IIoT应用潜能
    8月31日,触想重磅推出全新Z系列高性能、扩展型工控机——TPC05/06/07-WIPC,提供标准版/双卡槽/四卡槽3款机型选择。作为边缘计算、机器视觉、AI智能和工业应用的理想机型,Z系列工控机支持Intel®第12/13/14代Core™i3/i5/i7/i9处理器,最多搭载4个PCIe/PCI的扩展能力,可外接多......
  • PARTII-Oracle数据访问-SQL
    7.SQL7.1.SQL简介7.1.1.SQL数据访问7.1.2.SQL标准7.2.SQL语句概述7.2.1.数据定义语言(DDL)7.2.2.数据操作语言(DML)7.2.3.事务控制语句7.2.4.会话控制语句7.2.5.7.3.优化器概述7.3.1.优化器用途7.3.2.优化器的组件7.3.3.访问路径7.3.4.优化器统计信息7.3......
  • 【STM32+HAL库】---- 硬件IIC驱动0.96OLED
    硬件开发板:STM32G0B1RET6软件平台:cubemax+keil+VScode内容原著声明代码借鉴学习于以下文章:STM32使用硬件IIC驱动0.96寸4针IOLED显示器(HAL库)1新建cubemax工程1.1配置系统时钟RCC1.2配置引脚1.3导出工程略…2代码2.1OLED_IIC_Config.h/*************......
  • 回溯——7.子集II
    力扣题目链接给定一个可能包含重复元素的整数数组nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。示例:输入:[1,2,2]输出:[[2],[1],[1,2,2],[2,2],[1,2],[]]解题思路总结:排序:首先对数组进行排序,便于之后的重复元素跳过处理。回溯法:通过递归遍......
  • 软设每日打卡——霍夫曼编码将频繁出现的字符釆用短编码,出现频率较低的字符采用长编码
    【题目】霍夫曼编码将频繁出现的字符釆用短编码,出现频率较低的字符采用长编码。具体        的操作过程为:i)以每个字符的出现频率作为关键字构建最小优先级队列;ii)取出关键        字最小的两个结点生成子树,根节点的关键字为孩子节点关键字之和,并将根节点......