文章目录
- 53. 最大子数组和
- 解法1——DP
- 解法2——分治(维护区间、类似线段树的思想)
- 2216. 美化数组的最少删除数(贪心)
- 2304. 网格中的最小路径代价
- 1410. HTML 实体解析器(模拟)
- 2824. 统计和小于目标的下标对数目(简单Q1)
- 1457. 二叉树中的伪回文路径
- 828. 统计子串中的唯一字符——贡献法
53. 最大子数组和
https://leetcode.cn/problems/maximum-subarray/description/?envType=daily-question&envId=2023-11-20
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
解法1——DP
class Solution {
public int maxSubArray(int[] nums) {
int ans = Integer.MIN_VALUE, s = 0;
for (int x: nums) {
s += x;
ans = Math.max(ans, s);
if (s < 0) s = 0;
}
return ans;
}
}
解法2——分治(维护区间、类似线段树的思想)
https://leetcode.cn/problems/maximum-subarray/?envType=daily-question&envId=2023-11-20
class Solution {
public int maxSubArray(int[] nums) {
return getInfo(nums, 0, nums.length - 1).mSum;
}
public Status getInfo(int[] a, int l, int r) {
if (l == r) {
return new Status(a[l], a[l], a[l], a[l]);
}
int m = (l + r) >> 1;
Status lSub = getInfo(a, l, m);
Status rSub = getInfo(a, m + 1, r);
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);
}
}
class Status {
// 左端点最大子段和、右端点最大子段和、最大子段和、区间和
int lSum, rSum, mSum, iSum;
public Status(int lSum, int rSum, int mSum, int iSum) {
this.lSum = lSum;
this.rSum = rSum;
this.mSum = mSum;
this.iSum = iSum;
}
}
2216. 美化数组的最少删除数(贪心)
https://leetcode.cn/problems/minimum-deletions-to-make-array-beautiful/description/?envType=daily-question&envId=2023-11-21
提示:
1 <= nums.length <= 10^5
0 <= nums[i] <= 10^5
只要需要删除,就删除好了。
为什么?——
因为假设不这样做而是删除完整的一组两个连续相同的数字,结果不会比删除nums[i+1]和nums[i+2]更好。
举例:1.1.2.3.3.4.4.5
删除1.1之后为2.3.3.4.4.5最后变成2.3.3.4.4.5
删除1之后为1.2.3.3.4.4.5,最后变成1.2.3.4.4.5
结果是一样的。
我们考虑连续的两组,a,b,c,d 四个数字。
假设a==b,此时有两种选择,
1.删除b
2.删除a和b
对于第一种方法,如果删除b,有两种情况:1.a == c,2.a!=c。
考虑更差的情况,需要同时删除c,得到a,d。
如果a == d,此时abcd四个数字均相同,两种删法结果一样。否则保留ad,删除2个。
对于第二种方法,删除a和b,得到c,d。考虑更差情况c和d一样,还需要多删除一个。
因此删除b的方法更优,即需要删除就删除一个。
class Solution {
public int minDeletion(int[] nums) {
int n = nums.length;
int last = 0, ans = 0; // last记录上一个下标
for (int i = 1; i < n; ++i) {
if (nums[i] == nums[last]) {
ans++;
} else {
last = i + 1;
i = last;
}
}
if ((n - ans) % 2 == 1) ans++;
return ans;
}
}
2304. 网格中的最小路径代价
https://leetcode.cn/problems/minimum-path-cost-in-a-grid/description/?envType=daily-question&envId=2023-11-22
提示:
m == grid.length
n == grid[i].length
2 <= m, n <= 50
grid 由从 0 到 m * n - 1 的不同整数组成
moveCost.length == m * n
moveCost[i].length == n
1 <= moveCost[i][j] <= 100
时间复杂度:
O
(
m
∗
n
2
)
O(m∗n^2)
O(m∗n2)
空间复杂度:
O
(
m
∗
n
)
O(m∗n)
O(m∗n)
class Solution {
public int minPathCost(int[][] grid, int[][] moveCost) {
int m = grid.length, n = grid[0].length;
int[][] dp = new int[m][n];
System.arraycopy(grid[0], 0, dp[0], 0, n);
for (int i = 1; i < m; ++i) { // 枚举每一层
Arrays.fill(dp[i], Integer.MAX_VALUE / 2);
for (int j = 0; j < n; ++j) { // 枚举每一列
for (int k = 0; k < n; ++k) { // 枚举上一层的每一列
dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + moveCost[grid[i - 1][k]][j] + grid[i][j]);
}
}
}
return Arrays.stream(dp[m - 1]).min().getAsInt();
}
}
1410. HTML 实体解析器(模拟)
https://leetcode.cn/problems/html-entity-parser/description/?envType=daily-question&envId=2023-11-23
提示:
1 <= text.length <= 10^5
字符串可能包含 256 个ASCII 字符中的任意字符。
模拟即可,里面用到了类似分组循环的技巧。
class Solution {
public String entityParser(String text) {
StringBuilder ans = new StringBuilder();
Map<String, String> m = Map.of("quot", "\"",
"apos", "'",
"amp", "&",
"gt", ">",
"lt", "<",
"frasl", "/");
for (int i = 0; i < text.length(); ++i) {
if (text.charAt(i) == '&') {
int j = i + 1;
while (j < text.length() && text.charAt(j) != ';') j++;
String k = text.substring(i + 1, j);
if (m.containsKey(k)) {
ans.append(m.get(k));
i = j;
}
else ans.append(text.charAt(i));
} else ans.append(text.charAt(i));
}
return ans.toString();
}
}
2824. 统计和小于目标的下标对数目(简单Q1)
https://editor.csdn.net/md/?articleId=134503372
提示:
1 <= nums.length == n <= 50
-50 <= nums[i], target <= 50
class Solution {
public int countPairs(List<Integer> nums, int target) {
int ans = 0, n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums.get(i) + nums.get(j) < target) ++ans;
}
}
return ans;
}
}
1457. 二叉树中的伪回文路径
https://leetcode.cn/problems/pseudo-palindromic-paths-in-a-binary-tree/description/?envType=daily-question&envId=2023-11-25
提示:
给定二叉树的节点数目在范围 [1, 10^5] 内
1 <= Node.val <= 9
如果能够构成回文串,那么利用异或的性质,最多只有一位有1。也就是最多只有一个数字出现了奇数次。
class Solution {
int ans = 0;
public int pseudoPalindromicPaths (TreeNode root) {
dfs(root, 0);
return ans;
}
public void dfs(TreeNode root, int x) {
x ^= 1 << root.val;
if (root.left == null && root.right == null) {
if (x == 0 || Integer.bitCount(x) == 1) ++ans;
}
if (root.left != null) dfs(root.left, x);
if (root.right != null) dfs(root.right, x);
}
}
一个相关题目可见:2791. 树中可以形成回文的路径数,位于:【力扣周赛】第 355 场周赛(构造&二分答案&异或前缀 状态压缩⭐树中可以形成回文的路径数)
828. 统计子串中的唯一字符——贡献法
https://leetcode.cn/problems/count-unique-characters-of-all-substrings-of-a-given-string/description/?envType=daily-question&envId=2023-11-26
提示:
1 <= s.length <= 10^5
s 只包含大写英文字符
每个字符可以作为唯一字符的范围是从 【前一个该字符出现的位置,后一个该字符出现的位置】。
假设当前字符的位置是i,它的可用范围是
(
l
,
r
)
(l,r)
(l,r),不包括
l
l
l 和
r
r
r,那么它的贡献是
(
i
−
l
)
∗
(
r
−
l
)
(i-l)*(r-l)
(i−l)∗(r−l)。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
class Solution {
public int uniqueLetterString(String s) {
// 计算每个字符的贡献
int n = s.length(), ans = 0;
// 将相同字符的下标放入同一列表
Map<Character, List<Integer>> m = new HashMap<>();
for (int i = 0; i < n; ++i) {
char ch = s.charAt(i);
if (!m.containsKey(ch)) {
m.put(ch, new ArrayList<>());
m.get(ch).add(-1);
}
m.get(ch).add(i);
}
// 计算答案
for (List<Integer> ls: m.values()) {
ls.add(n);
for (int i = 1; i < ls.size() - 1; ++i) {
ans += (ls.get(i) - ls.get(i - 1)) * (ls.get(i + 1) - ls.get(i));
}
}
return ans;
}
}