228. 汇总区间
给定一个 无重复元素 的 有序 整数数组 nums 。
返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。
列表中的每个区间范围 [a,b] 应该按如下格式输出:
“a->b” ,如果 a != b
“a” ,如果 a == b
示例 1:
输入:nums = [0,1,2,4,5,7]
输出:[“0->2”,“4->5”,“7”]
解释:区间范围是:
[0,2] --> “0->2”
[4,5] --> “4->5”
[7,7] --> “7”
示例 2:
输入:nums = [0,2,3,4,6,8,9]
输出:[“0”,“2->4”,“6”,“8->9”]
解释:区间范围是:
[0,0] --> “0”
[2,4] --> “2->4”
[6,6] --> “6”
[8,9] --> “8->9”
class Solution {
public List<String> summaryRanges(int[] nums) {
List<String> ans = new ArrayList<>();
int i = 0;
int n = nums.length;
while (i < n) {
int low = i; // 区间起始
i++;
// 找一个连续区间
while (i < n && nums[i] == nums[i - 1] + 1) {
i++;
}
int high = i - 1; // 区间结尾
// 拼接一个区间串
StringBuilder stringBuilder = new StringBuilder(Integer.toString(nums[low]));
if (low < high) {
stringBuilder.append("->");
stringBuilder.append(Integer.toString(nums[high]));
}
ans.add(stringBuilder.toString());
}
return ans;
}
}
2235. 两整数相加
添加链接描述
给你两个整数 num1 和 num2,返回这两个整数的和。
示例 1:
输入:num1 = 12, num2 = 5
输出:17
解释:num1 是 12,num2 是 5 ,它们的和是 12 + 5 = 17 ,因此返回 17 。
示例 2:
输入:num1 = -10, num2 = 4
输出:-6
解释:num1 + num2 = -6 ,因此返回 -6 。
class Solution {
// 不使用加法运算符
public int sum(int num1, int num2) {
while (num2 != 0) {
int carry = (num1 & num2) << 1; // 进位
num1 ^= num2; // 不进位的和
num2 = carry;
}
return num1;
}
public int sum1(int num1, int num2) {
return num1 + num2;
}
}
833. 字符串中的查找与替换
添加链接描述
你会得到一个字符串 s (索引从 0 开始),你必须对它执行 k 个替换操作。替换操作以三个长度均为 k 的并行数组给出:indices, sources, targets。
要完成第 i 个替换操作:
检查 子字符串 sources[i] 是否出现在 原字符串 s 的索引 indices[i] 处。
如果没有出现, 什么也不做 。
如果出现,则用 targets[i] 替换 该子字符串。
例如,如果 s = “abcd” , indices[i] = 0 , sources[i] = “ab”, targets[i] = “eee” ,那么替换的结果将是 “eeecd” 。
所有替换操作必须 同时 发生,这意味着替换操作不应该影响彼此的索引。测试用例保证元素间不会重叠 。
例如,一个 s = “abc” , indices = [0,1] , sources = [“ab”,“bc”] 的测试用例将不会生成,因为 “ab” 和 “bc” 替换重叠。
在对 s 执行所有替换操作后返回 结果字符串 。
子字符串 是字符串中连续的字符序列。
示例 1:
输入:s = “abcd”, indices = [0,2], sources = [“a”,“cd”], targets = [“eee”,“ffff”]
输出:“eeebffff”
解释:
“a” 从 s 中的索引 0 开始,所以它被替换为 “eee”。
“cd” 从 s 中的索引 2 开始,所以它被替换为 “ffff”。
示例 2:
输入:s = “abcd”, indices = [0,2], sources = [“ab”,“ec”], targets = [“eee”,“ffff”]
输出:“eeecd”
解释:
“ab” 从 s 中的索引 0 开始,所以它被替换为 “eee”。
“ec” 没有从原始的 S 中的索引 2 开始,所以它没有被替换。
class Solution {
public String findReplaceString(String s, int[] indices, String[] sources, String[] targets) {
int n = s.length();
int[] replace = new int[n]; // 保存哪个位置需要替换
Arrays.fill(replace, -1);
for (int k = 0; k < indices.length; k++) {
// startsWith 判断指定偏移量开头是否以指定字符串开头
if (s.startsWith(sources[k], indices[k])) {
replace[indices[k]] = k; // 记录需要替换的位置
}
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < n;) {
if (replace[i] >= 0) {
stringBuilder.append(targets[replace[i]]); // 替换成 targets,位置是 replace[i]
// i 替换成功,向后跳原来替换的字符串长度
// a b c d e f
// 替换 cd 为 ffff,a b ffff e f
// 替换成功,i 下一个从 e 比较,而不是 d
int index = replace[i];
i += sources[index].length();
} else {
stringBuilder.append(s.charAt(i));
i++;
}
}
return stringBuilder.toString();
}
}
1444. 切披萨的方案数
给你一个 rows x cols 大小的矩形披萨和一个整数 k ,矩形包含两种字符: ‘A’ (表示苹果)和 ‘.’ (表示空白格子)。你需要切披萨 k-1 次,得到 k 块披萨并送给别人。
切披萨的每一刀,先要选择是向垂直还是水平方向切,再在矩形的边界上选一个切的位置,将披萨一分为二。如果垂直地切披萨,那么需要把左边的部分送给一个人,如果水平地切,那么需要把上面的部分送给一个人。在切完最后一刀后,需要把剩下来的一块送给最后一个人。
请你返回确保每一块披萨包含 至少 一个苹果的切披萨方案数。由于答案可能是个很大的数字,请你返回它对 10^9 + 7 取余的结果。
示例 1:
输入:pizza = [“A…”,“AAA”,“…”], k = 3
输出:3
解释:上图展示了三种切披萨的方案。注意每一块披萨都至少包含一个苹果。
示例 2:
输入:pizza = [“A…”,“AA.”,“…”], k = 3
输出:1
示例 3:
输入:pizza = [“A…”,“A…”,“…”], k = 1
输出:1
class Solution {
public int ways(String[] pizza, int k) {
int mod = 1_000_000_007;
int rows = pizza.length;
int cols = pizza[0].length();
int[][] apples = new int[rows + 1][cols + 1];
// 统计苹果分布数量
for (int i = rows - 1; i >= 0; i--) {
for (int j = cols - 1; j >= 0; j--) {
apples[i][j] = apples[i][j + 1] + apples[i + 1][j] - apples[i + 1][j + 1] +
(pizza[i].charAt(j) == 'A' ? 1 : 0); // 容斥原理
}
}
// 切披萨数
// 从右下角开始
// dp[i,j,k]的含义是披萨仅剩从坐标i,j开始的右下角部分,并且可以切k刀的方案数
int[][][] dp = new int[rows + 1][cols + 1][k + 1];
for (int kk = 1; kk <= k; kk++) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (kk == 1) {
if (apples[i][j] > 0) {
dp[i][j][kk] = 1;
}
} else {
// 水平方向切
for (int ii = i + 1; ii < rows; ii++) {
if (apples[i][j] > apples[ii][j]) { // 切出去上面的披萨的至少有一个苹果
dp[i][j][kk] = (dp[i][j][kk] + dp[ii][j][kk - 1]) % mod;
}
}
// 垂直方向切
for (int jj = j + 1; jj < cols; jj++) {
if (apples[i][j] > apples[i][jj]) {
dp[i][j][kk] = (dp[i][j][kk] + dp[i][jj][kk - 1]) % mod;
}
}
}
}
}
}
return dp[0][0][k];
}
}