Skip to content

Commit 31a9066

Browse files
Merge branch 'master' into feature/nextString
2 parents 9c7131e + 3563976 commit 31a9066

16 files changed

+586
-32
lines changed

problems/0005.最长回文子串.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,60 @@ public:
256256
* 时间复杂度:O(n^2)
257257
* 空间复杂度:O(1)
258258

259+
### Manacher 算法
259260

261+
Manacher 算法的关键在于高效利用回文的对称性,通过插入分隔符和维护中心、边界等信息,在线性时间内找到最长回文子串。这种方法避免了重复计算,是处理回文问题的最优解。
262+
263+
```c++
264+
//Manacher 算法
265+
class Solution {
266+
public:
267+
string longestPalindrome(string s) {
268+
// 预处理字符串,在每个字符之间插入 '#'
269+
string t = "#";
270+
for (char c : s) {
271+
t += c; // 添加字符
272+
t += '#';// 添加分隔符
273+
}
274+
int n = t.size();// 新字符串的长度
275+
vector<int> p(n, 0);// p[i] 表示以 t[i] 为中心的回文半径
276+
int center = 0, right = 0;// 当前回文的中心和右边界
277+
278+
279+
// 遍历预处理后的字符串
280+
for (int i = 0; i < n; i++) {
281+
// 如果当前索引在右边界内,利用对称性初始化 p[i]
282+
if (i < right) {
283+
p[i] = min(right - i, p[2 * center - i]);
284+
}
285+
// 尝试扩展回文
286+
while (i - p[i] - 1 >= 0 && i + p[i] + 1 < n && t[i - p[i] - 1] == t[i + p[i] + 1]) {
287+
p[i]++;// 增加回文半径
288+
}
289+
// 如果当前回文扩展超出右边界,更新中心和右边界
290+
if (i + p[i] > right) {
291+
center = i;// 更新中心
292+
right = i + p[i];// 更新右边界
293+
}
294+
}
295+
// 找到最大回文半径和对应的中心
296+
int maxLen = 0, centerIndex = 0;
297+
for (int i = 0; i < n; i++) {
298+
if (p[i] > maxLen) {
299+
maxLen = p[i];// 更新最大回文长度
300+
centerIndex = i;// 更新中心索引
301+
}
302+
}
303+
// 计算原字符串中回文子串的起始位置并返回
304+
return s.substr((centerIndex - maxLen) / 2, maxLen);
305+
}
306+
};
307+
```
308+
309+
310+
311+
* 时间复杂度:O(n)
312+
* 空间复杂度:O(n)
260313

261314
## 其他语言版本
262315

@@ -682,3 +735,4 @@ public class Solution {
682735
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
683736
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
684737
</a>
738+

problems/0112.路径总和.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -564,10 +564,10 @@ class Solution:
564564

565565
return False
566566

567-
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
567+
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
568568
if root is None:
569569
return False
570-
return self.traversal(root, sum - root.val)
570+
return self.traversal(root, targetSum - root.val)
571571
```
572572

573573
(版本二) 递归 + 精简
@@ -579,12 +579,12 @@ class Solution:
579579
# self.left = left
580580
# self.right = right
581581
class Solution:
582-
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
582+
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
583583
if not root:
584584
return False
585-
if not root.left and not root.right and sum == root.val:
585+
if not root.left and not root.right and targetSum == root.val:
586586
return True
587-
return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)
587+
return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)
588588

589589
```
590590
(版本三) 迭代
@@ -596,7 +596,7 @@ class Solution:
596596
# self.left = left
597597
# self.right = right
598598
class Solution:
599-
def hasPathSum(self, root: TreeNode, sum: int) -> bool:
599+
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
600600
if not root:
601601
return False
602602
# 此时栈里要放的是pair<节点指针,路径数值>
@@ -659,13 +659,13 @@ class Solution:
659659

660660
return
661661

662-
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
662+
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
663663
self.result.clear()
664664
self.path.clear()
665665
if not root:
666666
return self.result
667667
self.path.append(root.val) # 把根节点放进路径
668-
self.traversal(root, sum - root.val)
668+
self.traversal(root, targetSum - root.val)
669669
return self.result
670670
```
671671

@@ -678,7 +678,7 @@ class Solution:
678678
# self.left = left
679679
# self.right = right
680680
class Solution:
681-
def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
681+
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
682682

683683
result = []
684684
self.traversal(root, targetSum, [], result)
@@ -703,7 +703,7 @@ class Solution:
703703
# self.left = left
704704
# self.right = right
705705
class Solution:
706-
def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
706+
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
707707
if not root:
708708
return []
709709
stack = [(root, [root.val])]

problems/0134.加油站.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i
158158

159159
如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。
160160

161-
区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了
161+
区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择起始位置了
162162

163163

164164
**那么局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置**

problems/0151.翻转字符串里的单词.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,10 @@ class Solution {
440440
```Python
441441
class Solution:
442442
def reverseWords(self, s: str) -> str:
443-
# 删除前后空白
444-
s = s.strip()
445443
# 反转整个字符串
446444
s = s[::-1]
447445
# 将字符串拆分为单词,并反转每个单词
446+
# split()函数能够自动忽略多余的空白字符
448447
s = ' '.join(word[::-1] for word in s.split())
449448
return s
450449

@@ -1029,3 +1028,4 @@ public string ReverseWords(string s) {
10291028
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
10301029
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
10311030
</a>
1031+

problems/0459.重复的子字符串.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
如果有一个字符串s,在 s + s 拼接后, 不算首尾字符,如果能凑成s字符串,说明s 一定是重复子串组成。
6666

67-
如图,字符串s,图中数字为数组下标,在 s + s 拼接后, 不算首尾字符,中间凑成s字符串。
67+
如图,字符串s,图中数字为数组下标,在 s + s 拼接后, 不算首尾字符,中间凑成s字符串。 (图中数字为数组下标)
6868

6969
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910115555.png)
7070

@@ -163,9 +163,7 @@ KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一
163163
164164
如果一个字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串一定是字符串s的最小重复子串。
165165
166-
证明: 如果s 是由最小重复子串p组成。
167-
168-
即 s = n * p
166+
如果s 是由最小重复子串p组成,即 s = n * p
169167
170168
那么相同前后缀可以是这样:
171169
@@ -203,12 +201,14 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3
203201
204202
最长相等前后缀不包含的子串已经是字符串s的最小重复子串,那么字符串s一定由重复子串组成,这个不需要证明了。
205203
206-
关键是要要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。
204+
关键是要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。
207205
208-
情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串
206+
情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串,如图:
209207
210208
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240911110236.png)
211209
210+
图中:前后缀不包含的子串的长度 大于 字符串s的长度的 二分之一
211+
212212
--------------
213213
214214
情况二,最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,如图:
@@ -230,7 +230,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3
230230
即 s[0]s[1] 是最小重复子串
231231
232232
233-
以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能使最小重复子串
233+
以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能是最小重复子串
234234
235235
如果 s[0] 和 s[1] 也相同,同时 s[0]s[1]与s[2]s[3]相同,s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同,那么这个字符串就是有一个字符构成的字符串。
236236
@@ -246,7 +246,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3
246246
247247
或者说,自己举个例子,`aaaaaa`,这个字符串,他的最长相等前后缀是什么?
248248
249-
同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,就是一定是最小重复子串
249+
同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,最长相等前后缀不包含的子串一定是最小重复子串
250250
251251
----------------
252252
@@ -267,7 +267,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3
267267
268268
以上推导,可以得出 s[0],s[1],s[2] 与 s[3],s[4],s[5] 相同,s[3]s[4] 与 s[6]s[7]相同。
269269
270-
那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,就不是s的重复子串
270+
那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,最长相等前后缀不包含的子串就不是s的重复子串
271271
272272
-----------
273273
@@ -277,7 +277,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3
277277
278278
在必要条件,这个是 显而易见的,都已经假设 最长相等前后缀不包含的子串 是 s的最小重复子串了,那s必然是重复子串。
279279
280-
关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串。
280+
**关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串**
281281
282282
同上我们证明了,当 最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,那么不包含的子串 就是s的最小重复子串。
283283
@@ -312,7 +312,7 @@ next 数组记录的就是最长相同前后缀( [字符串:KMP算法精讲]
312312
313313
4可以被 12(字符串的长度) 整除,所以说明有重复的子字符串(asdf)。
314314
315-
### 打码实现
315+
### 代码实现
316316
317317
C++代码如下:(这里使用了前缀表统一减一的实现方式)
318318

problems/0513.找树左下角的值.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。
5757

58-
本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。
58+
本题还需要类里的两个全局变量,maxDepth用来记录最大深度,result记录最大深度最左节点的数值。
5959

6060
代码如下:
6161

problems/0518.零钱兑换II.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,23 +168,43 @@ for (int j = 0; j <= amount; j++) { // 遍历背包容量
168168
class Solution {
169169
public:
170170
int change(int amount, vector<int>& coins) {
171-
vector<int> dp(amount + 1, 0);
172-
dp[0] = 1;
171+
vector<uint64_t> dp(amount + 1, 0); // 防止相加数据超int
172+
dp[0] = 1; // 只有一种方式达到0
173173
for (int i = 0; i < coins.size(); i++) { // 遍历物品
174174
for (int j = coins[i]; j <= amount; j++) { // 遍历背包
175175
dp[j] += dp[j - coins[i]];
176176
}
177177
}
178-
return dp[amount];
178+
return dp[amount]; // 返回组合数
179179
}
180180
};
181181
```
182182
183+
C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
184+
183185
* 时间复杂度: O(mn),其中 m 是amount,n 是 coins 的长度
184186
* 空间复杂度: O(m)
185187
188+
为了防止相加的数据 超int 也可以这么写:
189+
190+
```CPP
191+
class Solution {
192+
public:
193+
int change(int amount, vector<int>& coins) {
194+
vector<int> dp(amount + 1, 0);
195+
dp[0] = 1; // 只有一种方式达到0
196+
for (int i = 0; i < coins.size(); i++) { // 遍历物品
197+
for (int j = coins[i]; j <= amount; j++) { // 遍历背包
198+
if (dp[j] < INT_MAX - dp[j - coins[i]]) { //防止相加数据超int
199+
dp[j] += dp[j - coins[i]];
200+
}
201+
}
202+
}
203+
return dp[amount]; // 返回组合数
204+
}
205+
};
206+
```
186207

187-
是不是发现代码如此精简
188208

189209
## 总结
190210

problems/0541.反转字符串II.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
因为要找的也就是每2 * k 区间的起点,这样写,程序会高效很多。
3939

40-
**所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章**
40+
**所以当需要固定规律一段一段去处理字符串的时候,要想想在for循环的表达式上做做文章**
4141

4242
性能如下:
4343
<img src='https://code-thinking.cdn.bcebos.com/pics/541_反转字符串II.png' width=600> </img></div>
@@ -505,3 +505,4 @@ impl Solution {
505505
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
506506
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
507507
</a>
508+

problems/kamacoder/0047.参会dijkstra朴素.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,65 @@ if __name__ == "__main__":
869869

870870
### Javascript
871871

872+
```js
873+
function dijkstra(grid, start, end) {
874+
const visited = Array.from({length: end + 1}, () => false)
875+
const minDist = Array.from({length: end + 1}, () => Number.MAX_VALUE)
876+
minDist[start] = 0
877+
878+
for (let i = 1 ; i < end + 1 ; i++) {
879+
let cur = -1
880+
let tempMinDist = Number.MAX_VALUE
881+
// 1. 找尋與起始點距離最近且未被訪的節點
882+
for (let j = 1 ; j < end + 1 ; j++) {
883+
if (!visited[j] && minDist[j] < tempMinDist) {
884+
cur = j
885+
tempMinDist = minDist[j]
886+
}
887+
}
888+
if (cur === -1) break;
889+
890+
// 2. 更新節點狀態為已拜訪
891+
visited[cur] = true
892+
893+
// 3. 更新未拜訪節點與起始點的最短距離
894+
for (let j = 1 ; j < end + 1 ; j++) {
895+
if(!visited[j] && grid[cur][j] != Number.MAX_VALUE
896+
&& grid[cur][j] + minDist[cur] < minDist[j]
897+
) {
898+
minDist[j] = grid[cur][j] + minDist[cur]
899+
}
900+
}
901+
}
902+
903+
return minDist[end] === Number.MAX_VALUE ? -1 : minDist[end]
904+
}
905+
906+
907+
async function main() {
908+
// 輸入
909+
const rl = require('readline').createInterface({ input: process.stdin })
910+
const iter = rl[Symbol.asyncIterator]()
911+
const readline = async () => (await iter.next()).value
912+
const [n, m] = (await readline()).split(" ").map(Number)
913+
const grid = Array.from({length: n + 1},
914+
() => Array.from({length:n + 1}, () => Number.MAX_VALUE))
915+
for (let i = 0 ; i < m ; i++) {
916+
const [s, e, w] = (await readline()).split(" ").map(Number)
917+
grid[s][e] = w
918+
}
919+
920+
// dijkstra
921+
const result = dijkstra(grid, 1, n)
922+
923+
// 輸出
924+
console.log(result)
925+
}
926+
927+
928+
main()
929+
```
930+
872931
### TypeScript
873932

874933
### PhP

0 commit comments

Comments
 (0)