题目描述:
给你一个整数数组 nums
和一个 非负 整数 k
。如果一个整数序列 seq
满足在范围下标范围 [0, seq.length - 2]
中存在 不超过 k
个下标 i
满足 seq[i] != seq[i + 1]
,那么我们称这个整数序列为 好 序列。
请你返回 nums
中 好 子序列 的最长长度
数据范围:
$1\le nums.len \le 5 \times 10^3, 1\le nums[i] \le 10^9, 0\le k \le \min(50, nums.len)$
题解:
子序列,不是子数组。子数组的话可以直接滑动窗口,子序列只能dp了。
考虑 $dp[i][k]$ 表示以 $nums[i]$ 结尾,且不相等的个数为 $k$ 的最大长度。
则可以得到
但是复杂度为 $O(n^2\times k)$ ,会超时。
考虑求前缀最大值优化。优化掉 $j$ 这层循环。
$dp[i][k]$ 需要使用 $dp[j][k - 1], dp[j][k]$ 也就是前一列的最大值,和当前列的最大值。 $k$ 从前往后,需要每一列的最大值。可以用一个长度为 $k + 1$ 的数组维护每一列的最大值。然后当遍历完一行之后,更新一下每列的最大值。
需要注意的是, $dp[j][k]$ 怎么优化掉?
因为 $dp[j][k]$ 只会在 $nums[j] = nums[i]$ 时出现,而且相等时肯定选最新的。因此可以使用一个 unordered_map
,直接把上一行拷贝过来。
然后再遍历所有的不相等的情况。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| auto optimize_cpp_stdio = []() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); return 0; }(); class Solution { public: const static int maxn = 1e5 + 10; const static int maxm = 1e5 + 10; const static long long mod = 1e9 + 7; const long long INF_LL = 0x3f3f3f3f3f3f3f3f; const int INF = 0x3f3f3f3f; int maximumLength(vector<int> &nums, int k) { int n = nums.size(); vector<vector<int>> dp(n + 1, vector<int>(k + 1)); for (int i = 0; i < n; ++i) { dp[i][0] = 1; } int ans = 1; for (int i = 1; i < n; ++i) { for (int kk = 0; kk <= min(k, i + 1); ++kk) { for (int j = 0; j < i; ++j) { if (nums[i] == nums[j]) { dp[i][kk] = max(dp[i][kk], dp[j][kk] + 1); } else { if (kk - 1 >= 0) dp[i][kk] = max(dp[i][kk], dp[j][kk - 1] + 1); } } ans = max(ans, dp[i][kk]); } } return ans; } };
|