Every day a Leetcode
题目来源:659. 分割数组为连续子序列
解法1:哈希 + 贪心
定义两个哈希表:
- numsCount:统计数组 nums 中各元素出现次数。
- tailCount:存储以数字 i 结尾的且符合题意的连续子序列个数。
算法:
- 先去寻找一个长度为3的连续子序列 i,i+1,i+2,找到后就将 numsCount[i],numsCount[i+1],numsCount[i+2] 中对应数字消耗 1 个(即 -1),并将 tail[i+2] 加 1,即以 i+2 结尾的子序列个数 +1。
- 如果后续发现有能够接在这个连续子序列的数字 i+3,则延长以 i+2 为结尾的连续子序列到 i+3,此时消耗 numsCount[i+3] 一个,由于子序列已延长,因此 tailCount[i+2] 减 1,tailCount[i+3] 加 1。
- 在不满足上面的情况下:
- 如果 numsCount[i] == 0,说明这个数字已经消耗完,可以不管了。
- 如果 numsCount[i] != 0,说明这个数字多出来了,且无法组成连续子序列,直接返回 false。
因此,只有检查到某个数时,这个数未被消耗完,且既不能和前面组成连续子序列,也不能和后面组成连续子序列时,无法分割。
关于上面 1 和 2 的优先度,也就是说,当遇到一个新数,是新建一个长度为 3 的子序列好,还是补充到原有子序列的末尾好?
优先开新序列,不够贪,会丢失部分子序列尾的位置,而这可能造成丢解。
优先补前面更贪心,这种做法会覆盖优先开新序列时的尾的位置,补这补这总能到,补的过程中出现的尾位置也不会丢失。
代码:
c
/*
* @lc app=leetcode.cn id=659 lang=cpp
*
* [659] 分割数组为连续子序列
*/
// @lc code=start
class Solution
{
public:
bool isPossible(vector<int> &nums)
{
if (nums.size() < 3)
return false;
unordered_map<int, int> numsCount, tailCount;
// 统计数组 nums 中各元素出现次数
for (int &num : nums)
numsCount[num]++;
// tailCount[i] 表示以 i 结尾的符合条件的子序列个数
for (int &num : nums)
{
if (numsCount[num] == 0)
continue;
else if (numsCount[num] > 0 && tailCount[num - 1] > 0)
{
// 1. 补充到已有子序列的尾部
numsCount[num] -= 1;
tailCount[num - 1] -= 1;
tailCount[num] += 1;
}
else if (numsCount[num] > 0 && numsCount[num + 1] > 0 && numsCount[num + 2] > 0)
{
// 2. 新建一条长度为 3 的子序列
// 注意 1 的优先级比 2 高
numsCount[num] -= 1;
numsCount[num + 1] -= 1;
numsCount[num + 2] -= 1;
tailCount[num + 2] += 1;
}
else
return false;
}
return true;
}
};
// @lc code=end
结果:
复杂度分析:
时间复杂度:O(n),其中 n 是数组 nums 的长度。
空间复杂度:O(n),其中 n 是数组 nums 的长度。