mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-05 22:59:31 +08:00
Update
This commit is contained in:
@ -107,7 +107,9 @@
|
|||||||
5. [数组:209.长度最小的子数组](./problems/0209.长度最小的子数组.md)
|
5. [数组:209.长度最小的子数组](./problems/0209.长度最小的子数组.md)
|
||||||
6. [数组:区间和](./problems/kamacoder/0058.区间和.md)
|
6. [数组:区间和](./problems/kamacoder/0058.区间和.md)
|
||||||
6. [数组:59.螺旋矩阵II](./problems/0059.螺旋矩阵II.md)
|
6. [数组:59.螺旋矩阵II](./problems/0059.螺旋矩阵II.md)
|
||||||
7. [数组:总结篇](./problems/数组总结篇.md)
|
7. [数组:区间和](./problems/kamacoder/0058.区间和.md)
|
||||||
|
8. [数组:开发商购买土地](./problems/kamacoder/0044.开发商购买土地.md)
|
||||||
|
9. [数组:总结篇](./problems/数组总结篇.md)
|
||||||
|
|
||||||
## 链表
|
## 链表
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
|
||||||
# 44. 开发商购买土地
|
# 44. 开发商购买土地
|
||||||
|
|
||||||
|
> 本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
|
||||||
|
|
||||||
|
[题目链接](https://kamacoder.com/problempage.php?pid=1044)
|
||||||
|
|
||||||
【题目描述】
|
【题目描述】
|
||||||
|
|
||||||
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
|
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
|
||||||
@ -57,7 +61,7 @@
|
|||||||
|
|
||||||
如果本题要求 任何两个行(或者列)之间的数值总和,大家在[0058.区间和](./0058.区间和.md) 的基础上 应该知道怎么求。
|
如果本题要求 任何两个行(或者列)之间的数值总和,大家在[0058.区间和](./0058.区间和.md) 的基础上 应该知道怎么求。
|
||||||
|
|
||||||
就是前缀和的思路,先统计好,前n行的和 q[n],如果要求矩阵 a 行到 b行 之间的总和,那么就 q[b] - q[a - 1]就好。
|
就是前缀和的思路,先统计好,前n行的和 q[n],如果要求矩阵 a行 到 b行 之间的总和,那么就 q[b] - q[a - 1]就好。
|
||||||
|
|
||||||
至于为什么是 a - 1,大家去看 [0058.区间和](./0058.区间和.md) 的分析,使用 前缀和 要注意 区间左右边的开闭情况。
|
至于为什么是 a - 1,大家去看 [0058.区间和](./0058.区间和.md) 的分析,使用 前缀和 要注意 区间左右边的开闭情况。
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
# 58. 区间和
|
# 58. 区间和
|
||||||
|
|
||||||
|
> 本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
|
||||||
|
|
||||||
[题目链接](https://kamacoder.com/problempage.php?pid=1070)
|
[题目链接](https://kamacoder.com/problempage.php?pid=1070)
|
||||||
|
|
||||||
题目描述
|
题目描述
|
||||||
@ -97,11 +99,11 @@ int main() {
|
|||||||
|
|
||||||
为什么呢?
|
为什么呢?
|
||||||
|
|
||||||
p[1] = vec[0] + vec[1];
|
`p[1] = vec[0] + vec[1];`
|
||||||
|
|
||||||
p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];
|
`p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];`
|
||||||
|
|
||||||
p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
|
`p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];`
|
||||||
|
|
||||||
这不就是我们要求的 下标 2 到下标 5 之间的累加和吗。
|
这不就是我们要求的 下标 2 到下标 5 之间的累加和吗。
|
||||||
|
|
||||||
@ -109,15 +111,17 @@ p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
p[5] - p[1] 就是 红色部分的区间和。
|
`p[5] - p[1]` 就是 红色部分的区间和。
|
||||||
|
|
||||||
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1)的操作。
|
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1) 的操作。
|
||||||
|
|
||||||
**特别注意**: 在使用前缀和求解的时候,要特别注意 求解区间。
|
**特别注意**: 在使用前缀和求解的时候,要特别注意 求解区间。
|
||||||
|
|
||||||
如上图,如果我们要求 区间下标 [2, 5] 的区间和,那么应该是 p[5] - p[1],而不是 p[5] - p[2]。
|
如上图,如果我们要求 区间下标 [2, 5] 的区间和,那么应该是 p[5] - p[1],而不是 p[5] - p[2]。
|
||||||
|
|
||||||
很多录友在使用前缀和的时候,分不清前缀和的区间,建议画一画图,模拟一下 思路会更清晰。
|
**很多录友在使用前缀和的时候,分不清前缀和的区间,建议画一画图,模拟一下 思路会更清晰**。
|
||||||
|
|
||||||
|
本题C++代码如下:
|
||||||
|
|
||||||
```CPP
|
```CPP
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -422,7 +422,8 @@ int main() {
|
|||||||
## 其他语言版本
|
## 其他语言版本
|
||||||
|
|
||||||
### Java
|
### Java
|
||||||
#### 邻接矩阵写法
|
|
||||||
|
邻接矩阵写法
|
||||||
```java
|
```java
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -477,7 +478,7 @@ public class Main {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 邻接表写法
|
邻接表写法
|
||||||
```java
|
```java
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -533,7 +534,7 @@ public class Main {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Python
|
### Python
|
||||||
#### 邻接矩阵写法
|
邻接矩阵写法
|
||||||
``` python
|
``` python
|
||||||
def dfs(graph, x, n, path, result):
|
def dfs(graph, x, n, path, result):
|
||||||
if x == n:
|
if x == n:
|
||||||
@ -566,7 +567,7 @@ if __name__ == "__main__":
|
|||||||
main()
|
main()
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 邻接表写法
|
邻接表写法
|
||||||
``` python
|
``` python
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
@ -225,6 +225,7 @@ int main() {
|
|||||||
vec.push_back(i);
|
vec.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 情况一、情况二
|
||||||
if (vec.size() > 0) {
|
if (vec.size() > 0) {
|
||||||
// 放在vec里的边已经按照倒叙放的,所以这里就优先删vec[0]这条边
|
// 放在vec里的边已经按照倒叙放的,所以这里就优先删vec[0]这条边
|
||||||
if (isTreeAfterRemoveEdge(edges, vec[0])) {
|
if (isTreeAfterRemoveEdge(edges, vec[0])) {
|
||||||
|
59
problems/kamacoder/0113.国际象棋.md
Normal file
59
problems/kamacoder/0113.国际象棋.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
# 113.国际象棋
|
||||||
|
|
||||||
|
广搜,但本题如果广搜枚举马和象的话会超时。
|
||||||
|
|
||||||
|
广搜要只枚举马的走位,同时判断是否在对角巷直接走象
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
const int N = 100005, mod = 1000000007;
|
||||||
|
using ll = long long;
|
||||||
|
int n, ans;
|
||||||
|
int dir[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}};
|
||||||
|
int main() {
|
||||||
|
int x1, y1, x2, y2;
|
||||||
|
cin >> n;
|
||||||
|
while (n--) {
|
||||||
|
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
|
||||||
|
if (x1 == x2 && y1 == y2) {
|
||||||
|
cout << 0 << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 判断象走一步到达
|
||||||
|
int d = abs(x1 - x2) - abs(y1 - y2);
|
||||||
|
if (!d) {cout << 1 << endl; continue;}
|
||||||
|
// 判断马走一步到达
|
||||||
|
bool one = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
|
||||||
|
if (dx == x2 && dy == y2) {
|
||||||
|
cout << 1 << endl;
|
||||||
|
one = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (one) continue;
|
||||||
|
// 接下来为两步的逻辑, 象走两步或者马走一步,象走一步
|
||||||
|
// 象直接两步可以到达,这个计算是不是同颜色的格子,象可以在两步到达所有同颜色的格子
|
||||||
|
int d2 = abs(x1 - x2) + abs(y1 - y2);
|
||||||
|
if (d2 % 2 == 0) {
|
||||||
|
cout << 2 << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 接下来判断马 + 象的组合
|
||||||
|
bool two = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
int dx = x1 + dir[i][0], dy = y1 + dir[i][1];
|
||||||
|
int d = abs(dx - x2) - abs(dy - y2);
|
||||||
|
if (!d) {cout << 2 << endl; two = true; break;}
|
||||||
|
}
|
||||||
|
if (two) continue;
|
||||||
|
// 剩下的格子全都是三步到达的
|
||||||
|
cout << 3 << endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
132
problems/kamacoder/0121.小红的区间翻转.md
Normal file
132
problems/kamacoder/0121.小红的区间翻转.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
# 121. 小红的区间翻转
|
||||||
|
|
||||||
|
比较暴力的方式,就是直接模拟, 枚举所有 区间,然后检查其翻转的情况。
|
||||||
|
|
||||||
|
在检查翻转的时候,需要一些代码优化,否则容易超时。
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
bool canTransform(const vector<int>& a, const vector<int>& b, int left, int right) {
|
||||||
|
// 提前检查翻转区间的值是否可以匹配
|
||||||
|
for (int i = left, j = right; i <= right; i++, j--) {
|
||||||
|
if (a[i] != b[j]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 检查翻转区间外的值是否匹配
|
||||||
|
for (int i = 0; i < left; i++) {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = right + 1; i < a.size(); i++) {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
|
||||||
|
vector<int> a(n);
|
||||||
|
vector<int> b(n);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cin >> a[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cin >> b[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// 遍历所有可能的区间
|
||||||
|
for (int left = 0; left < n; left++) {
|
||||||
|
for (int right = left; right < n; right++) {
|
||||||
|
// 检查翻转区间 [left, right] 后,a 是否可以变成 b
|
||||||
|
if (canTransform(a, b, left, right)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << count << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
也可以事先计算好,最长公共前缀,和最长公共后缀。
|
||||||
|
|
||||||
|
在公共前缀和公共后缀之间的部分进行翻转操作,这样我们可以减少很多不必要的翻转尝试。
|
||||||
|
|
||||||
|
通过在公共前缀和后缀之间的部分,找到可以通过翻转使得 a 和 b 相等的区间。
|
||||||
|
|
||||||
|
以下 为评论区 卡码网用户:码鬼的C++代码
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
vector<int> a(n), b(n);
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cin >> a[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
cin >> b[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<int> prefix(n, 0), suffix(n, 0);
|
||||||
|
|
||||||
|
// 计算前缀相等的位置
|
||||||
|
int p = 0;
|
||||||
|
while (p < n && a[p] == b[p]) {
|
||||||
|
prefix[p] = 1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算后缀相等的位置
|
||||||
|
int s = n - 1;
|
||||||
|
while (s >= 0 && a[s] == b[s]) {
|
||||||
|
suffix[s] = 1;
|
||||||
|
s--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// 遍历所有可能的区间
|
||||||
|
for (int i = 0; i < n - 1; i++) {
|
||||||
|
for (int j = i + 1; j < n; j++) {
|
||||||
|
// 判断前缀和后缀是否相等
|
||||||
|
if ((i == 0 || prefix[i - 1] == 1) && (j == n - 1 || suffix[j + 1] == 1)) {
|
||||||
|
// 判断翻转后的子数组是否和目标数组相同
|
||||||
|
bool is_palindrome = true;
|
||||||
|
for (int k = 0; k <= (j - i) / 2; k++) {
|
||||||
|
if (a[i + k] != b[j - k]) {
|
||||||
|
is_palindrome = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_palindrome) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << count << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
108
problems/kamacoder/0142.两个字符串的最小ASCII删除总和.md
Normal file
108
problems/kamacoder/0142.两个字符串的最小ASCII删除总和.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
# 142. 两个字符串的最小 ASCII 删除总和
|
||||||
|
|
||||||
|
本题和[代码随想录:两个字符串的删除操作](https://www.programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html) 思路基本是一样的。
|
||||||
|
|
||||||
|
属于编辑距离问题,如果想彻底了解,建议看看「代码随想录」的编辑距离总结篇。
|
||||||
|
|
||||||
|
本题dp数组含义:
|
||||||
|
|
||||||
|
dp[i][j] 表示 以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最小ASCII 删除总和。
|
||||||
|
|
||||||
|
如果 s1[i - 1] 与 s2[j - 1] 相同,则不用删:`dp[i][j] = dp[i - 1][j - 1]`
|
||||||
|
|
||||||
|
如果 s1[i - 1] 与 s2[j - 1] 不相同,删word1 的 最小删除和: `dp[i - 1][j] + s1[i - 1]` ,删word2的最小删除和: `dp[i][j - 1] + s2[j - 1]`
|
||||||
|
|
||||||
|
取最小值: `dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1])`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
int main() {
|
||||||
|
string s1, s2;
|
||||||
|
cin >> s1 >> s2;
|
||||||
|
vector<vector<int>> dp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
|
||||||
|
|
||||||
|
// s1 如果变成空串的最小删除ASCLL值综合
|
||||||
|
for (int i = 1; i <= s1.size(); i++) dp[i][0] = dp[i - 1][0] + s1[i - 1];
|
||||||
|
// s2 如果变成空串的最小删除ASCLL值综合
|
||||||
|
for (int j = 1; j <= s2.size(); j++) dp[0][j] = dp[0][j - 1] + s2[j - 1];
|
||||||
|
|
||||||
|
for (int i = 1; i <= s1.size(); i++) {
|
||||||
|
for (int j = 1; j <= s2.size(); j++) {
|
||||||
|
if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
|
||||||
|
else dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << dp[s1.size()][s2.size()] << endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Java
|
||||||
|
|
||||||
|
```Java
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
String s1 = scanner.nextLine();
|
||||||
|
String s2 = scanner.nextLine();
|
||||||
|
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
|
||||||
|
|
||||||
|
// s1 如果变成空串的最小删除ASCII值综合
|
||||||
|
for (int i = 1; i <= s1.length(); i++) {
|
||||||
|
dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1);
|
||||||
|
}
|
||||||
|
// s2 如果变成空串的最小删除ASCII值综合
|
||||||
|
for (int j = 1; j <= s2.length(); j++) {
|
||||||
|
dp[0][j] = dp[0][j - 1] + s2.charAt(j - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i <= s1.length(); i++) {
|
||||||
|
for (int j = 1; j <= s2.length(); j++) {
|
||||||
|
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
|
||||||
|
dp[i][j] = dp[i - 1][j - 1];
|
||||||
|
} else {
|
||||||
|
dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println(dp[s1.length()][s2.length()]);
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### python
|
||||||
|
|
||||||
|
```python
|
||||||
|
def min_delete_sum(s1: str, s2: str) -> int:
|
||||||
|
dp = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)]
|
||||||
|
|
||||||
|
# s1 如果变成空串的最小删除ASCII值综合
|
||||||
|
for i in range(1, len(s1) + 1):
|
||||||
|
dp[i][0] = dp[i - 1][0] + ord(s1[i - 1])
|
||||||
|
# s2 如果变成空串的最小删除ASCII值综合
|
||||||
|
for j in range(1, len(s2) + 1):
|
||||||
|
dp[0][j] = dp[0][j - 1] + ord(s2[j - 1])
|
||||||
|
|
||||||
|
for i in range(1, len(s1) + 1):
|
||||||
|
for j in range(1, len(s2) + 1):
|
||||||
|
if s1[i - 1] == s2[j - 1]:
|
||||||
|
dp[i][j] = dp[i - 1][j - 1]
|
||||||
|
else:
|
||||||
|
dp[i][j] = min(dp[i - 1][j] + ord(s1[i - 1]), dp[i][j - 1] + ord(s2[j - 1]))
|
||||||
|
|
||||||
|
return dp[len(s1)][len(s2)]
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
s1 = input().strip()
|
||||||
|
s2 = input().strip()
|
||||||
|
print(min_delete_sum(s1, s2))
|
||||||
|
```
|
237
problems/kamacoder/0143.最长同值路径.md
Normal file
237
problems/kamacoder/0143.最长同值路径.md
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# 143. 最长同值路径
|
||||||
|
|
||||||
|
|
||||||
|
本题两个考点:
|
||||||
|
|
||||||
|
1. 层序遍历构造二叉树
|
||||||
|
2. 树形dp,找出最长路径
|
||||||
|
|
||||||
|
对于写代码不多,或者动手能力比较差的录友,第一个 构造二叉树 基本就被卡主了。
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// 定义二叉树节点结构
|
||||||
|
struct TreeNode {
|
||||||
|
int val;
|
||||||
|
TreeNode* left;
|
||||||
|
TreeNode* right;
|
||||||
|
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据层序遍历数组构建二叉树
|
||||||
|
TreeNode* constructBinaryTree(const vector<string>& levelOrder) {
|
||||||
|
if (levelOrder.empty()) return NULL;
|
||||||
|
|
||||||
|
TreeNode* root = new TreeNode(stoi(levelOrder[0]));
|
||||||
|
queue<TreeNode*> q;
|
||||||
|
q.push(root);
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
while (!q.empty() && i < levelOrder.size()) {
|
||||||
|
TreeNode* current = q.front();
|
||||||
|
q.pop();
|
||||||
|
|
||||||
|
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
||||||
|
current->left = new TreeNode(stoi(levelOrder[i]));
|
||||||
|
q.push(current->left);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
||||||
|
current->right = new TreeNode(stoi(levelOrder[i]));
|
||||||
|
q.push(current->right);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
// 树形DP
|
||||||
|
int dfs(TreeNode* node) {
|
||||||
|
if (node == NULL) return 0;
|
||||||
|
int leftPath = dfs(node->left);
|
||||||
|
int rightPath = dfs(node->right);
|
||||||
|
|
||||||
|
int leftNum = 0, rightNum = 0;
|
||||||
|
if (node->left != NULL && node->left->val == node->val) {
|
||||||
|
leftNum = leftPath + 1;
|
||||||
|
}
|
||||||
|
if (node->right != NULL && node->right->val == node->val) {
|
||||||
|
rightNum = rightPath + 1;
|
||||||
|
}
|
||||||
|
result = max(result, leftNum + rightNum);
|
||||||
|
return max(leftNum, rightNum);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
vector<string> levelOrder(n);
|
||||||
|
for (int i = 0; i < n ; i++) cin >> levelOrder[i];
|
||||||
|
|
||||||
|
TreeNode* root = constructBinaryTree(levelOrder);
|
||||||
|
dfs(root);
|
||||||
|
cout << result << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Java
|
||||||
|
|
||||||
|
```Java
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
class TreeNode {
|
||||||
|
int val;
|
||||||
|
TreeNode left, right;
|
||||||
|
TreeNode(int x) {
|
||||||
|
val = x;
|
||||||
|
left = null;
|
||||||
|
right = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static int result = 0;
|
||||||
|
|
||||||
|
public static TreeNode constructBinaryTree(List<String> levelOrder) {
|
||||||
|
if (levelOrder.isEmpty()) return null;
|
||||||
|
|
||||||
|
TreeNode root = new TreeNode(Integer.parseInt(levelOrder.get(0)));
|
||||||
|
Queue<TreeNode> queue = new LinkedList<>();
|
||||||
|
queue.add(root);
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
while (!queue.isEmpty() && i < levelOrder.size()) {
|
||||||
|
TreeNode current = queue.poll();
|
||||||
|
|
||||||
|
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
|
||||||
|
current.left = new TreeNode(Integer.parseInt(levelOrder.get(i)));
|
||||||
|
queue.add(current.left);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) {
|
||||||
|
current.right = new TreeNode(Integer.parseInt(levelOrder.get(i)));
|
||||||
|
queue.add(current.right);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dfs(TreeNode node) {
|
||||||
|
if (node == null) return 0;
|
||||||
|
int leftPath = dfs(node.left);
|
||||||
|
int rightPath = dfs(node.right);
|
||||||
|
|
||||||
|
int leftNum = 0, rightNum = 0;
|
||||||
|
if (node.left != null && node.left.val == node.val) {
|
||||||
|
leftNum = leftPath + 1;
|
||||||
|
}
|
||||||
|
if (node.right != null && node.right.val == node.val) {
|
||||||
|
rightNum = rightPath + 1;
|
||||||
|
}
|
||||||
|
result = Math.max(result, leftNum + rightNum);
|
||||||
|
return Math.max(leftNum, rightNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner sc = new Scanner(System.in);
|
||||||
|
int n = sc.nextInt();
|
||||||
|
sc.nextLine(); // consume the newline character
|
||||||
|
List<String> levelOrder = new ArrayList<>();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
levelOrder.add(sc.next());
|
||||||
|
}
|
||||||
|
TreeNode root = constructBinaryTree(levelOrder);
|
||||||
|
dfs(root);
|
||||||
|
System.out.println(result);
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### python
|
||||||
|
|
||||||
|
```python
|
||||||
|
from typing import List, Optional
|
||||||
|
from collections import deque
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class TreeNode:
|
||||||
|
def __init__(self, val: int = 0, left: 'TreeNode' = None, right: 'TreeNode' = None):
|
||||||
|
self.val = val
|
||||||
|
self.left = left
|
||||||
|
self.right = right
|
||||||
|
|
||||||
|
def construct_binary_tree(level_order: List[str]) -> Optional[TreeNode]:
|
||||||
|
if not level_order:
|
||||||
|
return None
|
||||||
|
|
||||||
|
root = TreeNode(int(level_order[0]))
|
||||||
|
queue = deque([root])
|
||||||
|
i = 1
|
||||||
|
|
||||||
|
while queue and i < len(level_order):
|
||||||
|
current = queue.popleft()
|
||||||
|
|
||||||
|
if i < len(level_order) and level_order[i] != "null":
|
||||||
|
current.left = TreeNode(int(level_order[i]))
|
||||||
|
queue.append(current.left)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i < len(level_order) and level_order[i] != "null":
|
||||||
|
current.right = TreeNode(int(level_order[i]))
|
||||||
|
queue.append(current.right)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
def dfs(node: Optional[TreeNode]) -> int:
|
||||||
|
global result
|
||||||
|
if node is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
left_path = dfs(node.left)
|
||||||
|
right_path = dfs(node.right)
|
||||||
|
|
||||||
|
left_num = right_num = 0
|
||||||
|
if node.left is not None and node.left.val == node.val:
|
||||||
|
left_num = left_path + 1
|
||||||
|
if node.right is not None and node.right.val == node.val:
|
||||||
|
right_num = right_path + 1
|
||||||
|
|
||||||
|
result = max(result, left_num + right_num)
|
||||||
|
return max(left_num, right_num)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
input = sys.stdin.read
|
||||||
|
data = input().strip().split()
|
||||||
|
|
||||||
|
n = int(data[0])
|
||||||
|
level_order = data[1:]
|
||||||
|
|
||||||
|
root = construct_binary_tree(level_order)
|
||||||
|
dfs(root)
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
|
||||||
|
```
|
66
problems/kamacoder/0144.字典序最小的01字符串.md
Normal file
66
problems/kamacoder/0144.字典序最小的01字符串.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
# 0144.字典序最小的01字符串
|
||||||
|
|
||||||
|
贪心思路:移动尽可能 移动前面的1 ,这样可以是 字典序最小
|
||||||
|
|
||||||
|
从前到后遍历,遇到 0 ,就用前面的 1 来交换
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
int main() {
|
||||||
|
int n,k;
|
||||||
|
cin >> n >> k;
|
||||||
|
string s;
|
||||||
|
cin >> s;
|
||||||
|
for(int i = 0; i < n && k > 0; i++) {
|
||||||
|
if(s[i] == '0') {
|
||||||
|
// 开始用前面的 1 来交换
|
||||||
|
int j = i;
|
||||||
|
while(j > 0 && s[j - 1] == '1' && k > 0) {
|
||||||
|
swap(s[j], s[j - 1]);
|
||||||
|
--j;
|
||||||
|
--k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << s << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Java:
|
||||||
|
|
||||||
|
```Java
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
int n = scanner.nextInt();
|
||||||
|
int k = scanner.nextInt();
|
||||||
|
scanner.nextLine(); // 消耗掉换行符
|
||||||
|
String s = scanner.nextLine();
|
||||||
|
char[] ch = s.toCharArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < n && k > 0; i++) {
|
||||||
|
if (ch[i] == '0') {
|
||||||
|
// 开始用前面的 1 来交换
|
||||||
|
int j = i;
|
||||||
|
while (j > 0 && ch[j - 1] == '1' && k > 0) {
|
||||||
|
char tmp = ch[j];
|
||||||
|
ch[j] = ch[j - 1];
|
||||||
|
ch[j - 1] = tmp;
|
||||||
|
j--;
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(new String(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
98
problems/kamacoder/0145.数组子序列的排列.md
Normal file
98
problems/kamacoder/0145.数组子序列的排列.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
# 145. 数组子序列的排列
|
||||||
|
|
||||||
|
每个元素出现的次数相乘就可以了。
|
||||||
|
|
||||||
|
注意 “长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。” ,题目中有n个元素,所以我们要统计的就是 1 到 n 元素出现的个数。
|
||||||
|
|
||||||
|
因为如果有一个元素x 大于n了, 那不可能出现 长度为x的数组 且 1 到 x 每个元素都出现过。
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include "bits/stdc++.h"
|
||||||
|
using namespace std;
|
||||||
|
int main(){
|
||||||
|
int n;
|
||||||
|
int x;
|
||||||
|
cin >> n;
|
||||||
|
unordered_map<int, int> umap;
|
||||||
|
for(int i = 0; i < n; ++i){
|
||||||
|
cin >> x;
|
||||||
|
if(umap.find(x) != umap.end()) umap[x]++;
|
||||||
|
else umap[x] = 1;
|
||||||
|
}
|
||||||
|
long long res = 0;
|
||||||
|
long long num = 1;
|
||||||
|
for (int i = 1; i <= n; i++) {
|
||||||
|
if (umap.find(i) == umap.end()) break; // 如果i都没出现,后面得数也不能 1 到 m 每个元素都出现过
|
||||||
|
num = (num * umap[i]) % 1000000007;
|
||||||
|
res += num;
|
||||||
|
res %= 1000000007;
|
||||||
|
}
|
||||||
|
cout << res << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```Java
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner sc = new Scanner(System.in);
|
||||||
|
int n = sc.nextInt();
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int x = sc.nextInt();
|
||||||
|
map.put(x, map.getOrDefault(x, 0) + 1);
|
||||||
|
}
|
||||||
|
long res = 0;
|
||||||
|
long num = 1;
|
||||||
|
for (int i = 1; i <= n; i++) {
|
||||||
|
if (!map.containsKey(i)) break; // 如果i都没出现,后面得数也不能1到m每个元素都出现过
|
||||||
|
num = (num * map.get(i)) % 1000000007;
|
||||||
|
res += num;
|
||||||
|
res %= 1000000007;
|
||||||
|
}
|
||||||
|
System.out.println(res);
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
input = sys.stdin.read
|
||||||
|
data = input().split()
|
||||||
|
|
||||||
|
n = int(data[0])
|
||||||
|
umap = {}
|
||||||
|
|
||||||
|
for i in range(1, n + 1):
|
||||||
|
x = int(data[i])
|
||||||
|
if x in umap:
|
||||||
|
umap[x] += 1
|
||||||
|
else:
|
||||||
|
umap[x] = 1
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
num = 1
|
||||||
|
MOD = 1000000007
|
||||||
|
|
||||||
|
for i in range(1, n + 1):
|
||||||
|
if i not in umap:
|
||||||
|
break # 如果i都没出现,后面得数也不能1到m每个元素都出现过
|
||||||
|
num = (num * umap[i]) % MOD
|
||||||
|
res = (res + num) % MOD
|
||||||
|
|
||||||
|
print(res)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
```
|
65
problems/kamacoder/0146.传送树.md
Normal file
65
problems/kamacoder/0146.传送树.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 146. 传送树
|
||||||
|
|
||||||
|
本题题意是比较绕的,我后面给补上了 【提示信息】对 题目输出样例讲解一下,相对会容易理解的多。
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
vector<vector<int>> edge; // 邻接表来存图
|
||||||
|
vector<int> nxt;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 递归函数,用于找到每个节点的下一个传送门节点,并记录在nxt数组中。
|
||||||
|
* 遍历当前节点的所有子节点,递归调用findNext以确保子节点的nxt值已经计算出来。
|
||||||
|
* 更新当前节点的nxt值为其子节点中编号最小的节点。
|
||||||
|
* 如果当前节点是叶子节点(即没有子节点),则将其nxt值设置为自身。
|
||||||
|
*/
|
||||||
|
void findNext(int node) {
|
||||||
|
for (int v : edge[node]) {
|
||||||
|
findNext(v);
|
||||||
|
if (nxt[node] == -1 || nxt[node] > min(v, nxt[v])) {
|
||||||
|
nxt[node] = min(v, nxt[v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 叶子节点
|
||||||
|
if (nxt[node] == -1) {
|
||||||
|
nxt[node] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算从节点u出发经过若干次传送门到达叶子节点所需的步数。
|
||||||
|
// 通过不断访问nxt节点,直到到达叶子节点,记录访问的节点数。
|
||||||
|
int get(int u) {
|
||||||
|
int cnt = 1;
|
||||||
|
while (nxt[u] != u) {
|
||||||
|
cnt++;
|
||||||
|
u = nxt[u];
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
cin >> n;
|
||||||
|
edge.resize(n + 1);
|
||||||
|
nxt.resize(n + 1, -1);
|
||||||
|
for (int i = 1; i <= n; ++i) {
|
||||||
|
int a, b;
|
||||||
|
cin >> a >> b;
|
||||||
|
edge[a].push_back(b);
|
||||||
|
}
|
||||||
|
findNext(1);
|
||||||
|
for (int i = 1; i <= n; ++i) {
|
||||||
|
cout << get(i) << ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
40
problems/kamacoder/0147.三珠互斥.md
Normal file
40
problems/kamacoder/0147.三珠互斥.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看
|
||||||
|
2. 分别求出三个红珠子之间的距离
|
||||||
|
3. 对这三段距离从小到大排序 y1, y2, y3
|
||||||
|
4. 如果第一段距离y1 小于k,说明需要交换 k - y 次, 同理 第二段距离y2 小于k,说明需要交换 k - y2 次
|
||||||
|
5. y1 y2 都调整好了,不用计算y3,因为 y3是距离最大
|
||||||
|
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include<bits/stdc++.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int t;
|
||||||
|
cin >> t;
|
||||||
|
int n, k, a1, a2, a3;
|
||||||
|
vector<int> dis(3);
|
||||||
|
|
||||||
|
while (t--) {
|
||||||
|
cin >> n >> k >> a1 >> a2 >> a3;
|
||||||
|
if(k * 3 > n){
|
||||||
|
cout << -1 << endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dis[0] = min(abs(a1 - a2), n - abs(a1 - a2));
|
||||||
|
dis[1] = min(abs(a1 - a3), n - abs(a1 - a3));
|
||||||
|
dis[2] = min(abs(a3 - a2), n - abs(a3 - a2));
|
||||||
|
|
||||||
|
sort(dis.begin(), dis.end());
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if (dis[0] < k) result += (k - dis[0]);
|
||||||
|
if (dis[1] < k) result += (k - dis[1]);
|
||||||
|
|
||||||
|
cout << result << endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
53
problems/kamacoder/0148.扑克牌同花顺.md
Normal file
53
problems/kamacoder/0148.扑克牌同花顺.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
首先我们要定义一个结构体,来存放我们的数据
|
||||||
|
|
||||||
|
`map<花色,{同一花色牌集合,同一花色的牌对应的牌数量}>`
|
||||||
|
|
||||||
|
再遍历 每一个花色下,每一个牌 的数量
|
||||||
|
|
||||||
|
代码如下详细注释:
|
||||||
|
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include<bits/stdc++.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
string cards[] = {"H","S","D","C"};
|
||||||
|
typedef long long ll;
|
||||||
|
struct color
|
||||||
|
{
|
||||||
|
set<int> st; // 同一花色 牌的集合
|
||||||
|
map<int, ll> cnt; // 同一花色 牌对应的数量
|
||||||
|
};
|
||||||
|
unordered_map<string, color> umap;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int x, y;
|
||||||
|
string card;
|
||||||
|
cin >> x >> y >> card;
|
||||||
|
umap[card].st.insert(x);
|
||||||
|
umap[card].cnt[x] += y;
|
||||||
|
}
|
||||||
|
ll sum = 0;
|
||||||
|
// 遍历每一个花色
|
||||||
|
for (string cardOne : cards) {
|
||||||
|
color colorOne = umap[cardOne];
|
||||||
|
// 遍历 同花色 每一个牌
|
||||||
|
for (int number : colorOne.st) {
|
||||||
|
ll numberCount = colorOne.cnt[number]; // 获取牌为number的数量是 numberCount
|
||||||
|
|
||||||
|
// 统计 number 到 number + 4 都是否有牌,用cal 把 number 到number+4 的数量记下来
|
||||||
|
ll cal = numberCount;
|
||||||
|
for (int j = number + 1; j <= number + 4; j++) cal = min(cal, colorOne.cnt[j]);
|
||||||
|
// 统计结果
|
||||||
|
sum += cal;
|
||||||
|
// 把统计过的同花顺数量减下去
|
||||||
|
for (int j = number + 1; j <= number + 4; j++) colorOne.cnt[j] -= cal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << sum << endl;
|
||||||
|
}
|
||||||
|
```
|
46
problems/kamacoder/0150.极长连续段的权值.md
Normal file
46
problems/kamacoder/0150.极长连续段的权值.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
|
||||||
|
按照动态规划的思路,每增加一位,对已有结果会有什么变化呢?
|
||||||
|
|
||||||
|
输入 字符串s 里面有 0 和 1
|
||||||
|
|
||||||
|
我们先计算出 s[i-1] 的 子串权值和 a
|
||||||
|
|
||||||
|
如果 s[i] 和 s[i - 1] 相同
|
||||||
|
|
||||||
|
下标0 到 i 这段字符串 的子串权值和就是 a + 1,即算上s[i]本身的连续串数量加上
|
||||||
|
|
||||||
|
如果 s[i] 和 s[i - 1] 不相同
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
string s;
|
||||||
|
cin >> s;
|
||||||
|
|
||||||
|
long long result = 1;
|
||||||
|
long long a = 1;
|
||||||
|
|
||||||
|
for (int i = 1; i < n; ++i) {
|
||||||
|
if (s[i] == s[i - 1]) {
|
||||||
|
a += 1;
|
||||||
|
result += a;
|
||||||
|
} else {
|
||||||
|
a = a + i + 1;
|
||||||
|
result += a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << result << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
本题和[代码随想录:两个字符串的删除操作](https://www.programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html) 思路基本是一样的。
|
|
||||||
|
|
||||||
|
|
||||||
```CPP
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
int main() {
|
|
||||||
string s1, s2;
|
|
||||||
cin >> s1 >> s2;
|
|
||||||
vector<vector<int>> dp(s1.size() + 1, vector<int>(s2.size() + 1, 0));
|
|
||||||
|
|
||||||
// s1 如果变成空串的最小删除ASCLL值综合
|
|
||||||
for (int i = 1; i <= s1.size(); i++) dp[i][0] = dp[i - 1][0] + s1[i - 1];
|
|
||||||
// s2 如果变成空串的最小删除ASCLL值综合
|
|
||||||
for (int j = 1; j <= s2.size(); j++) dp[0][j] = dp[0][j - 1] + s2[j - 1];
|
|
||||||
|
|
||||||
for (int i = 1; i <= s1.size(); i++) {
|
|
||||||
for (int j = 1; j <= s2.size(); j++) {
|
|
||||||
if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
|
|
||||||
else dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cout << dp[s1.size()][s2.size()] << endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
56
problems/kamacoder/好数组.md
Normal file
56
problems/kamacoder/好数组.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
|
||||||
|
算是贪心
|
||||||
|
|
||||||
|
整体思路是移动到中间位置(中位数),一定是 移动次数最小的。
|
||||||
|
|
||||||
|
有一个数可以不改变,对数组排序之后, 最小数 和 最大数 一定是移动次数最多的,所以分别保留最小 和 最大的不变。
|
||||||
|
|
||||||
|
中间可能有两个位置,所以要计算中间偏前 和 中间偏后的
|
||||||
|
|
||||||
|
代码如下:
|
||||||
|
|
||||||
|
```CPP
|
||||||
|
|
||||||
|
#include<bits/stdc++.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n;
|
||||||
|
cin >> n;
|
||||||
|
vector<long> arr(n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
cin >> arr[i];
|
||||||
|
}
|
||||||
|
sort(arr.begin(), arr.end());
|
||||||
|
|
||||||
|
if (arr[0] == arr[n - 1]) {
|
||||||
|
cout << 1 << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
long cnt = 0L;
|
||||||
|
long cnt1 = 0L;
|
||||||
|
|
||||||
|
// 如果要保留一个不改变,要不不改最小的,要不不改最大的。
|
||||||
|
|
||||||
|
// 取中间偏前的位置
|
||||||
|
long mid = arr[(n - 2) / 2];
|
||||||
|
|
||||||
|
// 不改最大的
|
||||||
|
for (int i = 0; i < n - 1; i++) {
|
||||||
|
cnt += abs(arr[i] - mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取中间偏后的位置
|
||||||
|
mid = arr[n / 2];
|
||||||
|
|
||||||
|
// 不改最小的
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
cnt1 += abs(arr[i] - mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << min(cnt, cnt1) << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
@ -1,109 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Scanner sc = new Scanner(System.in);
|
|
||||||
int n = sc.nextInt();
|
|
||||||
int[] a = new int[n];
|
|
||||||
int[] b = new int[n];
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
a[i] = sc.nextInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
b[i] = sc.nextInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int p = -1, s = -1;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
if (a[i] == b[i]) p = i;
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = n - 1 ; j >= 0 ; j--) {
|
|
||||||
if (a[j] == b[j]) s = j;
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean[][] dp = new boolean[n][n];
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < n; j++) {
|
|
||||||
for (int i = j ; i >= 0 ; i--) {
|
|
||||||
if (i == j) dp[i][j] = a[i] == b[i];
|
|
||||||
else if (i + 1 == j) dp[i][j] = (a[i] == b[j] && a[j] == b[i]);
|
|
||||||
else {
|
|
||||||
dp[i][j] = dp[i+1][j-1] && (a[i] == b[j] && a[j] == b[i]);
|
|
||||||
}
|
|
||||||
if (dp[i][j] && (i == 0 || p >= i-1) && (j == n - 1 || j+1 >= s)) res++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Scanner sc = new Scanner(System.in);
|
|
||||||
int n = sc.nextInt();
|
|
||||||
|
|
||||||
int[] a = new int[n];
|
|
||||||
int[] b = new int[n];
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
a[i] = sc.nextInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
b[i] = sc.nextInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
// 遍历所有可能的区间
|
|
||||||
for (int left = 0; left < n; left++) {
|
|
||||||
for (int right = left; right < n; right++) {
|
|
||||||
// 检查翻转区间 [left, right] 后,a 是否可以变成 b
|
|
||||||
if (canTransform(a, b, left, right)) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean canTransform(int[] a, int[] b, int left, int right) {
|
|
||||||
// 提前检查翻转区间的值是否可以匹配
|
|
||||||
for (int i = left, j = right; i <= right; i++, j--) {
|
|
||||||
if (a[i] != b[j]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查翻转区间外的值是否匹配
|
|
||||||
for (int i = 0; i < left; i++) {
|
|
||||||
if (a[i] != b[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = right + 1; i < a.length; i++) {
|
|
||||||
if (a[i] != b[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```CPP
|
|
||||||
#include <iostream>
|
|
||||||
#include <queue>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// 定义二叉树节点结构
|
|
||||||
struct TreeNode {
|
|
||||||
int val;
|
|
||||||
TreeNode* left;
|
|
||||||
TreeNode* right;
|
|
||||||
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 根据层序遍历数组构建二叉树
|
|
||||||
TreeNode* constructBinaryTree(const vector<string>& levelOrder) {
|
|
||||||
if (levelOrder.empty()) return NULL;
|
|
||||||
|
|
||||||
TreeNode* root = new TreeNode(stoi(levelOrder[0]));
|
|
||||||
queue<TreeNode*> q;
|
|
||||||
q.push(root);
|
|
||||||
int i = 1;
|
|
||||||
|
|
||||||
while (!q.empty() && i < levelOrder.size()) {
|
|
||||||
TreeNode* current = q.front();
|
|
||||||
q.pop();
|
|
||||||
|
|
||||||
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
|
||||||
current->left = new TreeNode(stoi(levelOrder[i]));
|
|
||||||
q.push(current->left);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if (i < levelOrder.size() && levelOrder[i] != "null") {
|
|
||||||
current->right = new TreeNode(stoi(levelOrder[i]));
|
|
||||||
q.push(current->right);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
int dfs(TreeNode* node) {
|
|
||||||
if (node == NULL) return 0;
|
|
||||||
int leftPath = dfs(node->left);
|
|
||||||
int rightPath = dfs(node->right);
|
|
||||||
|
|
||||||
int leftNum = 0, rightNum = 0;
|
|
||||||
if (node->left != NULL && node->left->val == node->val) {
|
|
||||||
leftNum = leftPath + 1;
|
|
||||||
}
|
|
||||||
if (node->right != NULL && node->right->val == node->val) {
|
|
||||||
rightNum = rightPath + 1;
|
|
||||||
}
|
|
||||||
result = max(result, leftNum + rightNum);
|
|
||||||
return max(leftNum, rightNum);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int n;
|
|
||||||
cin >> n;
|
|
||||||
vector<string> levelOrder(n);
|
|
||||||
for (int i = 0; i < n ; i++) cin >> levelOrder[i];
|
|
||||||
|
|
||||||
TreeNode* root = constructBinaryTree(levelOrder);
|
|
||||||
dfs(root);
|
|
||||||
cout << result << endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -117,6 +117,13 @@
|
|||||||
|
|
||||||
相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实**真正解决题目的代码都是简洁的,或者有原则性的**,大家可以在这道题目中体会到这一点。
|
相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实**真正解决题目的代码都是简洁的,或者有原则性的**,大家可以在这道题目中体会到这一点。
|
||||||
|
|
||||||
|
### 前缀和
|
||||||
|
|
||||||
|
> 代码随想录后续补充题目
|
||||||
|
|
||||||
|
* [数组:求取区间和](https://programmercarl.com/kamacoder/0058.区间和.html)
|
||||||
|
|
||||||
|
前缀和的思路其实很简单,但非常实用,如果没接触过的录友,也很难想到这个解法维度,所以 这是开阔思路 而难度又不高的好题。
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user