【Lintcode】1103. Split Array into Consecutive Subsequences(配数学证明)

题目地址:

https://www.lintcode.com/problem/1103/description

给定一个长 n n n的升序数组 A A A,问其是否能被划分为若干连续的序列(指的是形如 x , x + 1 , x + 2 , . . . x,x+1,x+2,... x,x+1,x+2,...这样的序列),每个位置的数字只能用一次,并且每个序列长度不能小于 3 3 3

首先开个哈希表,存一下 A A A里的各个数字出现的次数,以便查找。我们维护一个HashMap,存的是已经得到的长度不少于 3 3 3的各个序列的最后一个数字及其出现的次数(即以该数字为结尾的序列的个数)。设遍历到了 A [ i ] A[i] A[i],考虑怎么处理 A [ i ] A[i] A[i]。如果HashMap里存在 A [ i ] − 1 A[i]-1 A[i]1,那么优先将其接到那个以 A [ i ] − 1 A[i]-1 A[i]1结尾的子序列后面;否则看一下 A [ i ] + 1 A[i]+1 A[i]+1 A [ i ] + 2 A[i]+2 A[i]+2是否还有剩余没用的,如果有,则用之,得到一个以 A [ i ] + 2 A[i]+2 A[i]+2结尾的长 3 3 3的序列;否则说明 A [ i ] A[i] A[i]没地方放了,则无解。这实际上是一种贪心的选择,需要证明其正确性。如果某个解是新开了一个以 A [ i ] A[i] A[i]为开头的序列的话,那么这个序列一定能接到 A [ i ] − 1 A[i]-1 A[i]1结尾的序列后面(因为这个序列长度至少是 3 3 3),从而也得到了一个合法解,从而贪心策略是可以得到一个合法解的。

严格证明如下:
数学归纳法。设对长 n n n的数组贪心是能得到正确答案的,接下来考虑长 n + 1 n+1 n+1的数组。首先考虑前 n n n个数的子数组,由归纳假设,贪心法能得到正确答案。如果对于 n + 1 n+1 n+1的数组真实结论是有解,考虑最后一个数字怎么放,如果贪心法判断出前 n n n个数字也是有解,那么一定存在一个序列末尾是 A [ n ] − 1 A[n]-1 A[n]1,从而贪心法会把它接到那个序列后面,从而贪心法对 n + 1 n+1 n+1也得出有解,结论正确;如果贪心法判断出前 n n n个数字是无解,那么说明 A [ n ] A[n] A[n]添上去才能得到一个长 3 3 3的序列,那么在遍历到 A [ n ] − 2 A[n]-2 A[n]2的时候就会把 A [ n ] A[n] A[n]囊括进某个序列中了,同样结论正确;如果真实结论是无解,由于贪心法如果判断出了有解,则也会给出一个方案,如果真实结论是无解,则贪心法必给不出方案,所以贪心法也会得出无解的结论,结论正确。综上贪心法能得出正确结论。

代码如下:

import java.util.HashMap;
import java.util.Map;

public class Solution {
    /**
     * @param nums: a list of integers
     * @return: return a boolean
     */
    public boolean isPossible(int[] nums) {
        Map<Integer, Integer> freq = new HashMap<>(), tail = new HashMap<>();
        for (int x : nums) {
            freq.put(x, freq.getOrDefault(x, 0) + 1);
        }
        
        for (int x : nums) {
            if (!freq.containsKey(x)) {
                continue;
            }
            
            if (tail.containsKey(x - 1)) {
                deleteOnce(freq, x);
                deleteOnce(tail, x - 1);
                tail.put(x, tail.getOrDefault(x, 0) + 1);
            } else if (freq.containsKey(x + 1) && freq.containsKey(x + 2)) {
                deleteOnce(freq, x);
                deleteOnce(freq, x + 1);
                deleteOnce(freq, x + 2);
                tail.put(x + 2, tail.getOrDefault(x + 2, 0) + 1);
            } else {
                return false;
            }
        }
        
        return true;
    }
    
    private void deleteOnce(Map<Integer, Integer> map, int x) {
        map.put(x, map.get(x) - 1);
        if (map.get(x) == 0) {
            map.remove(x);
        }
    }
}

时空复杂度 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值