最大子序和
给定一个整数数组 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[i−x]+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;
}
}