@@ -2490,5 +2490,237 @@ class Solution:
2490
2490
res = i + 1
2491
2491
continue
2492
2492
2493
+ return res
2494
+ ```
2495
+
2496
+ ## 第 159 场周赛
2497
+
2498
+ [ 点击前往第 159 场周赛] ( https://leetcode-cn.com/contest/weekly-contest-159 )
2499
+
2500
+ ### 5230. 缀点成线
2501
+
2502
+ [ 原题链接] ( https://leetcode-cn.com/contest/weekly-contest-159/problems/check-if-it-is-a-straight-line/ )
2503
+
2504
+ 取出前两个点根据 $y = kx + b$ 求解直线方程,再将后续的点套入方程中判断是否在直线上。
2505
+
2506
+ ``` python
2507
+ class Solution :
2508
+ def checkStraightLine (self , coordinates : List[List[int ]]) -> bool :
2509
+ x1, y1 = coordinates[0 ][0 ], coordinates[0 ][1 ]
2510
+ x2, y2 = coordinates[1 ][0 ], coordinates[1 ][1 ]
2511
+ # 求斜率 k
2512
+ if x1 == x2:
2513
+ k = 0
2514
+ else :
2515
+ k = (y2 - y1) / (x2 - x1)
2516
+ b = y1 - k * x1
2517
+
2518
+ for i in range (2 , len (coordinates)):
2519
+ x = coordinates[i][0 ]
2520
+ y = coordinates[i][1 ]
2521
+ if k * x + b != y:
2522
+ return False
2523
+
2524
+ return True
2525
+ ```
2526
+
2527
+ ### 5231. 删除子文件夹
2528
+
2529
+ [ 原题链接] ( https://leetcode-cn.com/contest/weekly-contest-159/problems/remove-sub-folders-from-the-filesystem/ )
2530
+
2531
+ #### 思路
2532
+
2533
+ 对 ` folder ` 按字典序排序,如果目录 ` x ` 在 ` folder ` 中存在根目录,那么这个根目录必定在 ` x ` 之前。
2534
+
2535
+ 1 . 将第一个目录作为根目录
2536
+ 2 . 从第二个目录开始遍历,判断 ` folder[i] ` 是否以当前根目录为前缀:
2537
+ - 是:` folder[i] ` 是子目录,跳过该目录,不记录到结果中
2538
+ - 否:将当前根目录更新为 ` folder[i] `
2539
+
2540
+ ps: 作为根目录时结尾加个 ` / ` ,不然 ` "/a/b/c" ` 就是 ` "/a/b/ca" ` 的前缀了。
2541
+
2542
+ #### 实现
2543
+
2544
+ ``` python
2545
+ class Solution :
2546
+ def removeSubfolders (self , folder : List[str ]) -> List[str ]:
2547
+ # 排序
2548
+ folder.sort()
2549
+ res = [folder[0 ]]
2550
+ # 将第一个目录设为当前根目录
2551
+ root = folder[0 ] + " /"
2552
+
2553
+ for i in range (1 , len (folder)):
2554
+ # 是否以 root 为前缀
2555
+ if not folder[i].startswith(root):
2556
+ # 不是子目录
2557
+ root = folder[i] + " /" # 更新根目录
2558
+ res.append(folder[i])
2559
+
2560
+ return res
2561
+ ```
2562
+
2563
+ 另外一种用字典树的笨方法:
2564
+
2565
+ ``` python
2566
+ class Solution :
2567
+ def removeSubfolders (self , folder : List[str ]) -> List[str ]:
2568
+ folder.sort()
2569
+ print (folder)
2570
+ m = dict ()
2571
+ res = []
2572
+ length = len (folder)
2573
+
2574
+ for i in range (length):
2575
+ letters = folder[i].split(' /' )
2576
+ is_child = True
2577
+ l_length = len (letters)
2578
+ find = m
2579
+ # 循环每个目录
2580
+ for j in range (1 , l_length):
2581
+ letter = letters[j]
2582
+ if letter not in find:
2583
+ # 不在
2584
+ find[letter] = dict ()
2585
+ is_child = False
2586
+ find = find[letter]
2587
+ break
2588
+ else :
2589
+ # 在
2590
+ find = find[letter]
2591
+ if ' end' in find: # 到达末尾
2592
+ is_child = True
2593
+ break
2594
+
2595
+ j += 1
2596
+ while j < l_length:
2597
+ tmp_letter = letters[j]
2598
+ if tmp_letter not in find:
2599
+ find[tmp_letter] = dict ()
2600
+ find = find[tmp_letter]
2601
+ j += 1
2602
+
2603
+ find[' end' ] = True
2604
+ # print(m)
2605
+
2606
+ if not is_child:
2607
+ res.append(folder[i])
2608
+
2609
+ return res
2610
+ ```
2611
+
2612
+ ### 5232. 替换子串得到平衡字符串
2613
+
2614
+ [ 原题链接] ( https://leetcode-cn.com/contest/weekly-contest-159/problems/replace-the-substring-for-balanced-string/ )
2615
+
2616
+ #### 思路
2617
+
2618
+ 题目要求 ` QWER ` 每个字符都需要出现 ` n / 4 ` 次,那么统计这 4 个字母出现的次数,超出 ` n / 4 ` 部分的即为需要删除的字母,即只要在字符串中找到包含所有多出字母的一个子串即可。用** 双指针** 圈定最终的子串范围。
2619
+
2620
+ #### 实现
2621
+
2622
+ ``` python
2623
+ class Solution :
2624
+ def balancedString (self , s : str ) -> int :
2625
+ length = len (s)
2626
+ n = length // 4
2627
+
2628
+ m = {" Q" : 0 , " W" : 1 , " E" : 2 , " R" : 3 }
2629
+ dp = [[0 , 0 , 0 , 0 ] for i in range (length)]
2630
+ c_count = [0 for _ in range (4 )]
2631
+ need = [0 for _ in range (4 )]
2632
+
2633
+ # 计算每个字母出现的个数
2634
+ for i in range (length):
2635
+ c = s[i]
2636
+ index = m[c]
2637
+ c_count[index] += 1
2638
+ dp[i] = [c_count[0 ], c_count[1 ], c_count[2 ], c_count[3 ]]
2639
+
2640
+ # 计算多出来的字母个数(即需要删除的个数)
2641
+ all_zero = True
2642
+ for i in range (4 ):
2643
+ count = c_count[i]
2644
+ if count > n:
2645
+ need[i] = count - n
2646
+ if need[i] != 0 :
2647
+ all_zero = False
2648
+ # 如果全为 0,说明不需要替换
2649
+ if all_zero:
2650
+ return 0
2651
+
2652
+ # 双指针,确定右指针所在的最小位置
2653
+ last = length - 1
2654
+ for i in range (length):
2655
+ if dp[i][0 ] >= need[0 ] and dp[i][1 ] >= need[1 ] and dp[i][2 ] >= need[2 ] and dp[i][3 ] >= need[3 ]:
2656
+ last = i
2657
+ break
2658
+
2659
+ i = 0
2660
+ j = last
2661
+ res = last + 1
2662
+ while i < length and j < length:
2663
+ # 两指针中间区域不满足要求,右指针右移
2664
+ if dp[j][0 ] - dp[i][0 ] < need[0 ] or dp[j][1 ] - dp[i][1 ] < need[1 ] or dp[j][2 ] - dp[i][2 ] < need[2 ] or dp[j][3 ] - dp[i][3 ] < need[3 ]:
2665
+ j += 1
2666
+ # 满足要求,左指针尽量向右移,看是否可以缩短字串长度
2667
+ else :
2668
+ res = min (j - i, res)
2669
+ i += 1
2670
+
2671
+ return res
2672
+ ```
2673
+
2674
+ ### 5233. 规划兼职工作
2675
+
2676
+ [ 原题链接] ( https://leetcode-cn.com/contest/weekly-contest-159/problems/maximum-profit-in-job-scheduling/ )
2677
+
2678
+ #### 思路
2679
+
2680
+ 动态规划。在开始计算之前,先把题目给出的数据整理一下。将 ` [startTime[i], endTime[i], profit[i]] ` 整理为数组,并按 ` startTime[i] - endTime[i] - profit[i]] ` 排序。
2681
+
2682
+ 用 ` dp[i] ` 表示完成第 ` i ` 份工作所能获得的最大收益。假设有第 ` x ` 份工作(` x < i ` ):
2683
+
2684
+ - 如果 ` x ` 与 ` i ` 时间上不重合,表示即可完成工作 ` x ` 又可以完成工作 ` i ` ,那么有:` dp[i] = max(dp[i], dp[x] + profit[i]) `
2685
+ - 如果 ` x ` 与 ` i ` 在时间上重合了,那么将无法完成工作 ` x `
2686
+
2687
+ 由此可见,` dp[i] ` 的值** 取决于在它前面所有与之时间不重合的工作收益** ,即:
2688
+
2689
+ ```
2690
+ dp[i] = max(dp[0], dp[1], ..., dp[j]) + profit[i] (`j` 为 `i` 之前最后一个与之时间区域不重合的工作)
2691
+ ```
2692
+
2693
+ 但是,如果每次都遍历 ` 0 ~ j ` 必然会超时,所以需要记录下时间区域不重合的工作所在的最大位置。
2694
+
2695
+ #### 实现
2696
+
2697
+ ``` python
2698
+ class Solution :
2699
+ def jobScheduling (self , startTime : List[int ], endTime : List[int ], profit : List[int ]) -> int :
2700
+ length = len (startTime)
2701
+ times = [[0 , 0 , 0 ] for _ in range (length)]
2702
+ for i in range (length):
2703
+ times[i][0 ] = startTime[i]
2704
+ times[i][1 ] = endTime[i]
2705
+ times[i][2 ] = profit[i]
2706
+ times.sort() # 排序
2707
+
2708
+ dp = [0 for i in range (length)]
2709
+
2710
+ res = 0
2711
+ s = 0
2712
+ pos = 0 # 标记位置
2713
+ for i in range (length):
2714
+ for j in range (pos, i):
2715
+ # 区间不重合
2716
+ if times[i][0 ] >= times[j][1 ]:
2717
+ # 移动 pos 的位置
2718
+ if j == pos:
2719
+ pos += 1
2720
+ s = max (s, dp[j])
2721
+
2722
+ dp[i] = s + times[i][2 ]
2723
+ res = max (res, dp[i])
2724
+
2493
2725
return res
2494
2726
```
0 commit comments