【状态机DP】力扣1493. 删掉一个元素以后全为 1 的最长子数组

给你一个二进制数组 nums ,你需要从中删掉一个元素。

请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。

如果不存在这样的子数组,请返回 0 。

提示 1:
输入:nums = [1,1,0,1]
输出:3
解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。

示例 2:
输入:nums = [0,1,1,1,0,1,1,0,1]
输出:5
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1] 。

示例 3:
输入:nums = [1,1,1]
输出:2
解释:你必须要删除一个元素。

在这里插入图片描述
递推

class Solution {
public:
    int longestSubarray(vector<int>& nums) {
        int n = nums.size();
        vector<int> pre(n), suf(n);
        pre[0] = nums[0];
        for(int i = 1; i < n; i++){
            pre[i] = nums[i] ? pre[i-1] + 1 : 0;
        }

        suf[n-1] = nums[n-1];
        for(int i = n-2; i >= 0; i--){
            suf[i] = nums[i] ? suf[i+1] + 1 : 0;
        }

        int maxSum = 0;
        for(int i = 0; i < n; i++){
            int preSum = i == 0 ? 0 : pre[i-1];
            int sufSum = i == n-1 ? 0 : suf[i+1];
            maxSum = max(maxSum, preSum + sufSum);
        }
        return maxSum;
    }
};

时间复杂度:O(n)。这里对原数组进行三次遍历,每次时间代价为 O(n),故渐进时间复杂度为 O(n)。

空间复杂度:O(n)。这里预处理 pre 和 suf 需要两个长度为 n 的数组。

我们可以定义两个数组,一个pre[i]来记录以i结尾的最大连续1的数量,一个suf[i]用来记录以i开头的最大连续1的数量,然后我们遍历每个元素作为删除的元素,然后就可以知道如果删除了这个元素,那么pre[i-1]和suf[i+1]的和就是最大连续1的数量。需要注意的是边界条件,由于当i=0的时候,pre[i-1]没有意义,当i=n-1的时候,suf[i+1]没有意义,所以我们要定义这两种情况。有人会问不能从i=1遍历到i<n-1吗?那么当nums=[0,1,0]的时候,输出为0,但实际上答案是1。

空间优化

class Solution {
public:
    int longestSubarray(vector<int>& nums) {
        int p0 = 0;
        int p1 = 0;
        int ans = 0;
        for(int num : nums){
            if(num == 0){
                p1 = p0;
                p0 = 0;
            }
            else{
                p1++;
                p0++;
            }
            ans = max(ans, p1);   
        }
        if(ans == nums.size()){
            ans--;
        }
        return ans;     
    }
};

时间复杂度:O(n)。这里对原数组进行一次遍历,时间代价为 O(n),故渐进时间复杂度为 O(n)。

空间复杂度:O(1)。

很好的优化,我们只需要定义两个变量p0和p1,p1用来表示删除一个元素0(也可以不删除)保持的最大的连续1的数量,p0代表的是0之间的1的数量。举个例子:

假设nums = {1, 1, 0, 1, 1, 1, 0, 1, 1}
初始化 p0 = 0,p1 = 0,ans = 0。

遍历第一个元素 1:

p0 和 p1 同时增加,p0 = 1,p1 = 1。
更新 ans = max(ans, p1) = 1。
遍历第二个元素 1:

p0 = 2,p1 = 2。
更新 ans = max(ans, p1) = 2。
遍历第三个元素 0:

p1 = p0 = 2(假设删除这个 0 后的连续 1 长度为 2),然后 p0 = 0。
更新 ans = max(ans, p1) = 2。
遍历第四个元素 1:

p0 = 1,p1 = 3(删除之前的 0 后,加上当前的 1,所以 p1 增加)。
更新 ans = max(ans, p1) = 3。
遍历第五个元素 1:

p0 = 2,p1 = 4。
更新 ans = max(ans, p1) = 4。
遍历第六个元素 1:

p0 = 3,p1 = 5。
更新 ans = max(ans, p1) = 5。
遍历第七个元素 0:

p1 = p0 = 3(假设删除这个 0 后的连续 1 长度为 3),然后 p0 = 0。
ans = max(ans, p1) = 5(不变)。
遍历第八个元素 1:

p0 = 1,p1 = 4。
更新 ans = max(ans, p1) = 5。
遍历第九个元素 1:

p0 = 2,p1 = 5。
ans = max(ans, p1) = 5(不变)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值