题目
https://www.lintcode.com/problem/1103
给定一个整数数组 nums. 现在需要将 nums 拆分成若干个 (至少1个) 子序列, 并且每个子序列至少包含 3 个连续的整数.
返回是否能做到这样的拆分.
nums.length 在 [1, 10000] 范围内.
nums 已经按照升序排序, 并且有可能含有重复的元素.
如果能够做到这样的拆分, 那么 nums 的每一个元素必须且只能存在于其中一个子序列中.
一个合法的子序列只能由连续的元素构成, 而且不能包含重复元素.
样例
样例1:
输入: [1,2,3,3,4,5]
输出: True
解释: 可以把数组拆分成两个子序列:
[1, 2, 3]
[3, 4, 5]
样例2:
输入: [1,2,3,3,4,4,5,5]
输出: True
解释: 可以把数组拆分成两个子序列:
[1, 2, 3, 4, 5]
[3, 4, 5]
样例3:
输入: [1,2,3,4,4,5]
输出: False
解释: 我们无法将其分成合法子序列。
思路
贪心
解题思路: 力扣同一道题是659
把场景想象成 拼团,当有新人进来,有两种选择:
能加入已有的团,就加入已有的团(加入得有规则)
不能加入,就必须拉两个新人开团,拉不到则算失败
带入代码逻辑则是:
遍历到元素 x ,先找是否有 x - 1 结尾的子序列,有则拼接到该子序列
没有则在数组中寻找 x + 1 和 x + 2 成立新的子序列,找不到则返回 false
思考的重心要从 数字 转到 子序列,即在任一时刻所有子序列都必须是合法的。
细节:
数组不适合直接找元素,用 countMap 存每个数字的个数,每用一个对应减一
没有必要维护整个子序列,用 tailCountMap 存所有子序列最后一个数字的个数
具体做法:需要两个哈希表:
cnt: cnt[i] 表示数字i还剩多少个
tail: tail[i] 表示以数字i结尾的合法子序列的数量
首先遍历整个 nums 数组统计出 cnt, 然后再次遍历 nums 数组, 对于每个整数 num, 它有两个去向:
如果 tail[num - 1] 大于 0, 那么直接将 num 添加到以 num - 1 结尾的子序列上即可,
这时以 num - 1 结尾的子序列数量 - 1, 并且以 num 结尾的子序列数量 + 1
如果 1 不成立, 那么 num 只能与比它大的数组成子序列, 即 num + 1 与 num + 2
如果 1, 2 都无法成立, 说明 num 无处可去, 返回 false 即可. 如果可以顺利遍历完 nums,
每个数都可以找到它的去向(或者1, 或者2), 返回 true 即可.
答案
public class Solution {
/**
* @param nums: a list of integers
* @return: return a boolean
*/
public boolean isPossible(int[] nums) {
/*
贪心
解题思路: 力扣同一道题是659
把场景想象成 拼团,当有新人进来,有两种选择:
能加入已有的团,就加入已有的团(加入得有规则)
不能加入,就必须拉两个新人开团,拉不到则算失败
带入代码逻辑则是:
遍历到元素 x ,先找是否有 x - 1 结尾的子序列,有则拼接到该子序列
没有则在数组中寻找 x + 1 和 x + 2 成立新的子序列,找不到则返回 false
思考的重心要从 数字 转到 子序列,即在任一时刻所有子序列都必须是合法的。
细节:
数组不适合直接找元素,用 countMap 存每个数字的个数,每用一个对应减一
没有必要维护整个子序列,用 tailCountMap 存所有子序列最后一个数字的个数
具体做法:需要两个哈希表:
cnt: cnt[i] 表示数字i还剩多少个
tail: tail[i] 表示以数字i结尾的合法子序列的数量
首先遍历整个 nums 数组统计出 cnt, 然后再次遍历 nums 数组, 对于每个整数 num, 它有两个去向:
如果 tail[num - 1] 大于 0, 那么直接将 num 添加到以 num - 1 结尾的子序列上即可,
这时以 num - 1 结尾的子序列数量 - 1, 并且以 num 结尾的子序列数量 + 1
如果 1 不成立, 那么 num 只能与比它大的数组成子序列, 即 num + 1 与 num + 2
如果 1, 2 都无法成立, 说明 num 无处可去, 返回 false 即可. 如果可以顺利遍历完 nums,
每个数都可以找到它的去向(或者1, 或者2), 返回 true 即可.
*/
//存储每个数字的个数
Map<Integer, Integer> cnt = new HashMap<>(); //k:v 表示数字k还剩多少个
for (int num : nums) {
if (!cnt.containsKey(num))
cnt.put(num, 0);
cnt.put(num, cnt.get(num) + 1);
}
Map<Integer, Integer> tail = new HashMap<>(); //k:v 表示以数字k结尾的合法子序列的数量
for (int x : nums) {
int count = cnt.get(x);
if (count > 0) {
cnt.put(x, count - 1);
int curcnt = tail.getOrDefault(x - 1, 0);
if (curcnt > 0) { //如果有x-1结尾的子序列,则拼接到子序列
tail.put(x - 1, curcnt - 1);
curcnt = tail.getOrDefault(x, 0) + 1;
tail.put(x, curcnt);
} else {
//没有则x+1,x+2成立新序列,找不到则返回false
int cnt1 = cnt.getOrDefault(x + 1, 0);
int cnt2 = cnt.getOrDefault(x + 2, 0);
if (cnt1 > 0 && cnt2 > 0) {
cnt.put(x + 1, cnt1 - 1);
cnt.put(x + 2, cnt2 - 1);
curcnt = tail.getOrDefault(x + 2, 0) + 1;
tail.put(x + 2, curcnt);
} else {
return false;
}
}
}
}
return true;
}
}