「这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战」。
LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode
921. 使括号有效的最少添加
给定一个由 '(' 和 ')' 括号组成的字符串 S,我们需要添加最少的括号( '(' 或是 ')',可以在任何位置),以使得到的括号字符串有效。
从形式上讲,只有满足下面几点之一,括号字符串才是有效的:
- 它是一个空字符串,或者
- 它可以被写成
AB(A与B连接), 其中A和B都是有效字符串,或者 - 它可以被写作
(A),其中A是有效字符串。
给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。
示例 1:
输入: "())"
输出: 1
示例 2:
输入: "((("
输出: 3
示例 3:
输入: "()"
输出: 0
示例 4:
输入: "()))(("
输出: 4
提示:
S.length <= 1000S只包含'('和')'字符。
PS:
因为要添加括号,因为只有左右号,不是左括号就是右括号
用-1和1进行记录,如果左括号小于右括号,然后需要添加括号,
如果右括号少,最后bal是不为0的,缺几个右括号bal就是几
class Solution {
public int minAddToMakeValid(String S) {
int ans = 0, bal = 0;
for (int i = 0; i < S.length(); ++i) {
bal += S.charAt(i) == '(' ? 1 : -1;
if (bal == -1) {
ans++;
bal++;
}
}
return ans + bal;
}
}
922. 按奇偶排序数组 II
给定一个非负整数数组 nums, nums 中一半整数是 奇数 ,一半整数是 偶数 。
对数组进行排序,以便当 nums[i] 为奇数时,i 也是 奇数 ;当 nums[i] 为偶数时, i 也是 偶数 。
你可以返回 任何满足上述条件的数组作为答案 。
示例 1:
输入: nums = [4,2,5,7]
输出: [4,5,2,7]
解释: [4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。
示例 2:
输入: nums = [2,3]
输出: [2,3]
提示:
2 <= nums.length <= 2 * 104nums.length是偶数nums中一半是偶数0 <= nums[i] <= 1000
进阶: 可以不使用额外空间解决问题吗?
PS:
先遍历数组,选择奇数,然后放到奇数下标,没找到一个奇数就index+1
重置index
如果是偶数就放到偶数下标
class Solution {
public int[] sortArrayByParityII(int[] nums) {
int n = nums.length;
int[] temp = new int[nums.length];
int index = 1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 1) {
temp[index] = nums[i];
index += 2;
}
}
index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 0) {
temp[index] = nums[i];
index += 2;
}
}
return temp;
}
}
923. 三数之和的多种可能
给定一个整数数组 arr ,以及一个整数 target 作为目标值,返回满足 i < j < k 且 arr[i] + arr[j] + arr[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 109 + 7 的模。
示例 1:
输入: arr = [1,1,2,2,3,3,4,4,5,5], target = 8
输出: 20
解释:
按值枚举(arr[i], arr[j], arr[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
示例 2:
输入: arr = [1,1,2,2,2,2], target = 5
输出: 12
解释:
arr[i] = 1, arr[j] = arr[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。
提示:
3 <= arr.length <= 30000 <= arr[i] <= 1000 <= target <= 300
class Solution {
public int threeSumMulti(int[] A, int target) {
int MOD = 1_000_000_007;
long ans = 0;
Arrays.sort(A);
for (int i = 0; i < A.length; ++i) {
// 我们用 i j k 三个凑成三数和
// 当 T = target - A[i]就有 A[j] + A[k] == T
int T = target - A[i];
int j = i+1, k = A.length - 1;
while (j < k) {
// 按照两数和的格式
if (A[j] + A[k] < T)
j++;
else if (A[j] + A[k] > T)
k--;
else if (A[j] != A[k]) { // A[j] + A[k] == T.
// left计数 A[j] == A[j+1] == A[j+2] == ...
// 右面相似
int left = 1, right = 1;
while (j+1 < k && A[j] == A[j+1]) {
left++;
j++;
}
while (k-1 > j && A[k] == A[k-1]) {
right++;
k--;
}
ans += left * right;
ans %= MOD;
j++;
k--;
} else {
// M = k - j + 1
// 有 M * (M-1) / 2 对
ans += (k-j+1) * (k-j) / 2;
ans %= MOD;
break;
}
}
}
return (int) ans;
}
}
925. 长按键入
你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。
你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回 True。
示例 1:
输入: name = "alex", typed = "aaleex"
输出: true
解释: 'alex' 中的 'a' 和 'e' 被长按。
示例 2:
输入: name = "saeed", typed = "ssaaedd"
输出: false
解释: 'e' 一定需要被键入两次,但在 typed 的输出中不是这样。
示例 3:
输入: name = "leelee", typed = "lleeelee"
输出: true
示例 4:
输入: name = "laiden", typed = "laiden"
输出: true
解释: 长按名字中的字符并不是必要的。
提示:
name.length <= 1000typed.length <= 1000name和typed的字符都是小写字母。
PS:
就是处理多个相同重复字符的问题
如果两个不相同,那么就对比当前字符和上一个字符是否相同
如果相同了就是长按了
如果不相同就是false
class Solution {
public boolean isLongPressedName(String name, String typed) {
int i = 0, j = 0;
while (j < typed.length()) {
if (i < name.length() && name.charAt(i) == typed.charAt(j)) {
i++;
j++;
} else if (j > 0 && typed.charAt(j) == typed.charAt(j - 1)) {
j++;
} else {
return false;
}
}
return i == name.length();
}
}
926. 将字符串翻转到单调递增
如果一个由 '0' 和 '1' 组成的字符串,是以一些 '0'(可能没有 '0')后面跟着一些 '1'(也可能没有 '1')的形式组成的,那么该字符串是单调递增的。
我们给出一个由字符 '0' 和 '1' 组成的字符串 S,我们可以将任何 '0' 翻转为 '1' 或者将 '1' 翻转为 '0'。
返回使 S 单调递增的最小翻转次数。
示例 1:
输入: s = "00110"
输出: 1
解释: 我们翻转最后一位得到 00111.
示例 2:
输入: s = "010110"
输出: 2
解释: 我们翻转得到 011111,或者是 000111。
示例 3:
输入: s = "00011000"
输出: 2
解释: 我们翻转得到 00000000。
提示:
1 <= s.length <= 105S中只包含字符'0'和'1'
PS:
先统计好,前缀和中 1 的数量
然后循环每一位,对于每一位
我们把前面的1变成0,然后再把后面的0变成1
这里就是减去后面那段范围 1 的数量就是 0
class Solution {
public int minFlipsMonoIncr(String S) {
int N = S.length();
int[] P = new int[N + 1];
for (int i = 0; i < N; ++i)
P[i+1] = P[i] + (S.charAt(i) == '1' ? 1 : 0);
int ans = Integer.MAX_VALUE;
for (int j = 0; j <= N; ++j) {
ans = Math.min(ans, P[j] + N-j-(P[N]-P[j]));
}
return ans;
}
}
929. 独特的电子邮件地址
每个 有效电子邮件地址 都由一个 本地名 和一个 域名 组成,以 '@' 符号分隔。除小写字母之外,电子邮件地址还可以含有一个或多个 '.' 或 '+' 。
- 例如,在
alice@leetcode.com中,alice是 本地名 ,而leetcode.com是 域名 。
如果在电子邮件地址的 本地名 部分中的某些字符之间添加句点('.'),则发往那里的邮件将会转发到本地名中没有点的同一地址。请注意,此规则 不适用于域名 。
- 例如,
"alice.z@leetcode.com”和“alicez@leetcode.com”会转发到同一电子邮件地址。
如果在 本地名 中添加加号('+'),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件。同样,此规则 不适用于域名 。
- 例如
m.y+name@email.com将转发到my@email.com。
可以同时使用这两个规则。
给你一个字符串数组 emails,我们会向每个 emails[i] 发送一封电子邮件。返回实际收到邮件的不同地址数目。
示例 1:
输入: emails = ["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com","testemail+david@lee.tcode.com"]
输出: 2
解释: 实际收到邮件的是 "testemail@leetcode.com" 和 "testemail@lee.tcode.com"。
示例 2:
输入: emails = ["a@leetcode.com","b@leetcode.com","c@leetcode.com"]
输出: 3
提示:
1 <= emails.length <= 1001 <= emails[i].length <= 100emails[i]由小写英文字母、'+'、'.'和'@'组成- 每个
emails[i]都包含有且仅有一个'@'字符 - 所有本地名和域名都不为空
- 本地名不会以
'+'字符作为开头
PS:
主要是处理加号的问题
先按照@符号分割,后面的不需要处理
前面的需要处理 + ,如果遇到 + 就把 + 后面的内容都删除掉,然后再把所有的 . 都删除
class Solution {
public int numUniqueEmails(String[] emails) {
Set<String> seen = new HashSet();
for (String email : emails) {
int i = email.indexOf('@');
String local = email.substring(0, i);
String rest = email.substring(i);
if (local.contains("+")) {
local = local.substring(0, local.indexOf('+'));
}
local = local.replaceAll("\\.", "");
seen.add(local + rest);
}
return seen.size();
}
}
930. 和相同的二元子数组
给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。
示例 1:
输入: nums = [1,0,1,0,1], goal = 2
输出: 4
解释:
有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]
示例 2:
输入: nums = [0,0,0,0,0], goal = 0
输出: 15
提示:
1 <= nums.length <= 3 * 104nums[i]不是0就是10 <= goal <= nums.length
PS:
其实就相当于两数之和
我们循环,然后记录前缀和
每到循环到一个地方就,因为要检测的是和为goal的
前缀和差如果有goal的,那么就是可以凑成的子数组,直接添加对应的数量
当前位置到前面每个和为sum -goal的位置都是和为 goal 的子数组
class Solution {
public int numSubarraysWithSum(int[] nums, int goal) {
int sum = 0;
Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();
int ret = 0;
for (int num : nums) {
cnt.put(sum, cnt.getOrDefault(sum, 0) + 1);
sum += num;
ret += cnt.getOrDefault(sum - goal, 0);
}
return ret;
}
}