题目地址:
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)。