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

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的升级版,对时间复杂度有了更高的要求。我们在上一篇题解中,给出了时间复杂度为 O ( n 2 ∗ k ) O(n^2*k) O(n2∗k)的解法。这次需要将时间复杂度降低到 O ( n ∗ k ) O(n*k) O(n∗k)。

解题思路

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

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

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

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

go 复制代码
			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版本:

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++版本:

cpp 复制代码
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版本:

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
相关推荐
m0_571957582 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
jrrz08287 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
南宫生8 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
Perishell8 小时前
无人机避障——大疆与Airsim中的角速度信息订阅获取
linux·动态规划·无人机
你好helloworld9 小时前
滑动窗口最大值
数据结构·算法·leetcode
sjsjs1111 小时前
【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
数据结构·leetcode
咕咕吖12 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎12 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
~yY…s<#>14 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
yannan2019031315 小时前
【算法】(Python)动态规划
python·算法·动态规划