2023.12.22力扣每日一题——得到山形数组的最少删除次数

题目来源

力扣每日一题;题序:1671

我的题解

方法一 前后缀分解+最长递增子序列

(参照题解中的:灵茶山艾府的解法

要想删除次数最少,山形数组的子序列长度越长越好。至于最终的最长是多少,这个可以将每一个位置都当做山峰来枚举,计算每个位置的山形子序列的最长长度。
山形子序列是一个严格递增的子序列+严格递减的子序列:

  • 定义pre[i]表示子序列最后一个数是nums[i]的最长严格递增子序列的长度
  • 定义suf[i]表示子序列第一个数是nums[i]的最长严格递减子序列的长度

这道题与昨日的山状数组有所不同,就是眼球山峰左右两侧必须有数字,所以pre[i]>=2并且suf[i]>=2。因而得到以nums[i]为山顶的最长山状子序列的长度为:pre[i]+suf[i]-1。
计算suf相当于求从右到左遍历nums求最长严格递增子序列,在计算时为了简便采用反转数组来实现。同理,计算pre相当于从左到右遍历nums求最长递增子序列。求最长严格递增子序列的方法有:动态规划和二分查找两种,这里采用二分查找。
最终结果:数组长度-山形子序列的最长长度

时间复杂度:O(nlogn)
空间复杂度:O(n)

 public int minimumMountainRemovals(int[] nums) {
        int n = nums.length;
        int[] suf = new int[n];
        List<Integer> g = new ArrayList<>();
        for (int i = n - 1; i > 0; i--) {
            int x = nums[i];
            int j = lowerBound(g, x);
            if (j == g.size()) {
                g.add(x);
            } else {
                g.set(j, x);
            }
            suf[i] = j + 1; // 从 nums[i] 开始的最长严格递减子序列的长度
        }

        int mx = 0;
        g.clear();
        for (int i = 0; i < n - 1; i++) {
            int x = nums[i];
            int j = lowerBound(g, x);
            if (j == g.size()) {
                g.add(x);
            } else {
                g.set(j, x);
            }
            int pre = j + 1; // 在 nums[i] 结束的最长严格递增子序列的长度
            if (pre >= 2 && suf[i] >= 2) {
                mx = Math.max(mx, pre + suf[i] - 1); // 减去重复的 nums[i]
            }
        }
        return n - mx;
    }

    private int lowerBound(List<Integer> g, int target) {
        int left = -1, right = g.size(); // 开区间 (left, right)
        while (left + 1 < right) { // 区间不为空
            // 循环不变量:
            // nums[left] < target
            // nums[right] >= target
            int mid = (left + right) >>> 1;
            if (g.get(mid) < target) {
                left = mid; // 范围缩小到 (mid, right)
            } else {
                right = mid; // 范围缩小到 (left, mid)
            }
        }
        return right; // 或者 left+1
    }

有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈😄~

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜菜的小彭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值