题目
给你一个整数数组 nums。
如果 nums[i] == i,则位置 i 被称为 固定点。
允许你从数组中删除 任意 数量的元素(包括零个)。在每次删除后,剩余元素 向左移动,并且下标从 0 开始重新分配。
返回一个整数,表示在执行任意次数的删除操作后,可以获得的 最大 固定点数量。
示例 1:
输入: nums = 0,2,1
输出: 2
解释:
- 删除
nums[1] = 2。数组变为[0, 1]。 - 现在,
nums[0] = 0且nums[1] = 1,因此两个下标都是固定点。 - 因此,答案为 2。
题解
思路学习:本题跟俄罗斯套娃信封问题的解题思路一致,解决本题核心思考:
要求i==numsi,当i增加的时候numsi也需要增加,也就是连续增长序列,也即本题求最长连续增长序列
当数组中numsi的元素x需要移动i-x步时,比x大的元素numsj=z需要的移动步数必须大于i-x步时才能让i,j同时成为固定点位,因为numsi移动的i-x步的时候,numsj也会移动的i-x步,如果numsj需要的移动步数小于numsi时,则两者不能同时成为固定点位。需要维护numi开始的最长增长序列长度和numsj开始的最长增长序列长度。
java
class Solution {
public int maxFixedPoints(int[] nums) {
List<int[]> a = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
int x = nums[i];
if (i >= x) {
a.add(new int[]{x, i - x});
}
}
return maxEnvelopes(a);
}
// 354. 俄罗斯套娃信封问题
private int maxEnvelopes(List<int[]> envelopes) {
//对位置排序,小的元素在前,大的元素在后
envelopes.sort((a, b) -> {
if (a[0] == b[0]) {
return b[1] - a[1];
}
return a[0] - b[0];
});
List<Integer> g = new ArrayList<>();
for (int[] e : envelopes) {
int h = e[1];
//最长增长序列模版思路:用二分在数组找对应的位置,然后更新元素值
// 允许 LIS 相邻元素相等
int j = upperBound(g, h);
if (j < g.size()) {
g.set(j, h);
} else {
g.add(h);
}
}
return g.size();
}
private int upperBound(List<Integer> g, int target) {
int left = -1, right = g.size(); // 开区间 (left, right)
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// nums[right] > target
// nums[left] <= target
int mid = left + (right - left) / 2;
if (g.get(mid) > target) {
right = mid; // 范围缩小到 (left, mid)
} else {
left = mid; // 范围缩小到 (mid, right)
}
}
return right;
}
}