lintcode 1103 · 分割数组为连续子序列【中等 哈希 同力扣659】

题目

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵长辉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值