【leetcode-DP】最大子序和/俄罗斯套娃信封问题/比特位计数/计数二进制子串

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:
输入:nums = [1]
输出:1

示例 3:
输入:nums = [0]
输出:0

示例 4:
输入:nums = [-1]
输出:-1

示例 5:
输入:nums = [-100000]
输出:-100000

动态规划

class Solution {
    public int maxSubArray(int[] nums) {
        int pre_max = 0, max_sum = nums[0];
        for (int n : nums) {
            pre_max = Math.max(pre_max + n, n);
            max_sum = Math.max(max_sum, pre_max);
        }
        return max_sum;
    }
}

分治

class Solution {
    public class Status {
        private int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;  // 以 l 为左端点的最大子段和
            this.rSum = rSum;  // 以 r 为右端点的最大子段和
            this.mSum = mSum;  // [l,r]内的最大子段和
            this.iSum = iSum;  // [l,r] 的区间和
        }
    }

    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    public Status getInfo(int[] nums, int left, int right) {
        if (left == right) {
            return new Status(nums[left], nums[left], nums[left], nums[left]);
        }
        int mid = (left + right) / 2;
        Status lSub = getInfo(nums, left, mid);
        Status rSub = getInfo(nums, mid + 1, right);
        return pushUp(lSub, rSub);
    }

    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }
}

俄罗斯套娃信封问题

给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。

示例 1:
输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出:3
解释:最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。

示例 2:
输入:envelopes = [[1,1],[1,1],[1,1]]
输出:1

动态规划

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);
        int n = envelopes.length;

        int[] dp = new int[n];
        int max = 0;
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
            for (int j = i - 1; j >= 0; j--)
                if (dp[j] >= dp[i] && envelopes[i][1] > envelopes[j][1]) 
                    dp[i] = dp[j] + 1;
            max = Math.max(max, dp[i]);
        }
        return max;
    }
}

单调序列

我们可以存储每一条单调递增的序列,但不需要存储它们的每一个值,只需保存其最大值及其位置即可。
在这里插入图片描述

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);
        int n = envelopes.length;

        List<Integer> list = new ArrayList<>();
        list.add(envelopes[0][1]);
        for (int i = 1, j = 0; i < n; i++) {
            j = 0;
            for (; j < list.size(); j++)
                if (envelopes[i][1] <= list.get(j)) {
                    list.set(j, envelopes[i][1]);
                    break;
                }
            if (j == list.size())
                list.add(envelopes[i][1]);
        }
        return list.size();
    }
}

单调序列+二分查找

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);
        int n = envelopes.length;

        int[] list = new int[n];
        list[0] = envelopes[0][1];
        int cur = 0;
        for (int i = 1; i < n; i++) {
            if (envelopes[i][1] > list[cur]) {
                list[++cur] = envelopes[i][1];
            } else {
                int index = binarySearch(list, 0, cur, envelopes[i][1]);
                list[index] = envelopes[i][1];
            }
        }
        return cur + 1;
    }

    private int binarySearch(int[] list, int left, int right, int target) {
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (list[mid] >= target)
                right = mid;
            else
                left = mid + 1;
        }
        return left;
    }
}

比特位计数

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:
输入: 2
输出: [0,1,1]

示例 2:
输入: 5
输出: [0,1,1,2,1,2]

遍历计数

class Solution {
    public int[] countBits(int num) {
        int[] counts = new int[num + 1];
        for (int i = 0; i <= num; i++)
            counts[i] = bitCount(i);
        return counts;
    }

    private int bitCount(int n) {
        n = (n >> 1 & 0x55555555) + (n & 0x55555555);
        n = (n >> 2 & 0x33333333) + (n & 0x33333333);
        n = (n >> 4 & 0x0f0f0f0f) + (n & 0x0f0f0f0f);
        n = (n >> 8 & 0x00ff00ff) + (n & 0x00ff00ff);
        n = (n >> 16 & 0x0000ffff) + (n & 0x0000ffff);
        return n;
    }
}

有效最高位

当遇到一个新的最高位 x x x,则其后面 i i i的比特位数为 c o u n t [ i ] = c o u n t [ i − x ] + 1 count[i]=count[i-x]+1 count[i]=count[ix]+1

class Solution {
    public int[] countBits(int num) {
        int[] counts = new int[num + 1];
        int highBit = 0;
        for (int i = 1; i <= num; i++) {
            if ((i & (i - 1)) == 0) 
                highBit = i;
            counts[i] = counts[i - highBit] + 1;
        }
        return counts;
    }
}

有效最低位

偶数 i i i与其右移1位的整数 i > > 1 i>>1 i>>1比特数相等,奇数 i i i与其右移1位的整数 i > > 1 i>>1 i>>1比特数大1。

class Solution {
    public int[] countBits(int num) {
        int[] counts = new int[num + 1];
        for (int i = 1; i <= num; i++) 
            counts[i] = counts[i >> 1] + (1 & i);
        return counts;
    }
}

最低设置位

KaTeX parse error: Expected 'EOF', got '&' at position 3: i &̲ (i -1)会去掉最右侧的1。

class Solution {
    public int[] countBits(int num) {
        int[] counts = new int[num + 1];
        for (int i = 1; i <= num; i++) 
            counts[i] = counts[i & (i - 1)] + 1;
        return counts;
    }
}

计数二进制子串

给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。
重复出现的子串要计算它们出现的次数。

示例 1 :
输入: “00110011”
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。
另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。

示例 2 :
输入: “10101”
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。

计数

计数连续的1或0的数量,相邻两个计数值的更小值为这两组连续的0、1能组成符合条件的子串数目。

class Solution {
    public int countBinarySubstrings(String s) {
        char[] array = s.toCharArray();
        int preCount = 0, curCount = 1;
        int result = 0, len = array.length;
        for (int i = 1; i < len; i++) {
            if (array[i] == array[i - 1]) {
                curCount++;
            } else {
                result += Math.min(preCount, curCount);
                preCount = curCount;
                curCount = 1;
            }
        }
        result += Math.min(preCount, curCount);
        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值