开新坑咯~
  作为一名 NOIP 退役选手,已经两年(?)没有好好研究算法了,就连 CCF CSP 也全靠吃着高中的老本。然而做人如果没有梦想,那和一条咸鱼有什么分别呢?  遂开此坑,有这时间去打原神每日,倒不如来 LeetCode 享受 A 题的快感(
  有啥办法能自动更新文章标题么🤔,经常忘记改这玩意
2022 年 12 月 03 日 
解题思路 
  500 个点直接排序……应该也不算慢吧🤔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl  Solution {    pub  fn  second_highest str : String ) -> i32  {         let  mut  digits: Vec <_> = str              .chars()             .filter(|it| it.is_ascii_digit())             .map(|it| it as  i32  - 0x30 )             .collect();         digits.sort_by_key(|it| -it);         digits.dedup();         if  let  Some (ans) = digits.get(1 ) {             *ans         } else  {             -1          }     } } 
2022 年 12 月 02 日 
解题思路 
  这是一个暴力解。对于每个位置,统计所有「1」到它的距离之和:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 impl  Solution {    pub  fn  min_operations String ) -> Vec <i32 > {         let  ones: Vec <_> = boxes             .chars()             .enumerate()             .filter(|it| it.1  == '1' )             .map(|it| it.0  as  i32 )             .collect();         (0 ..boxes.len() as  i32 )             .map(|it| ones.iter().map(|x| (it - x).abs()).sum())             .collect()     } } 
  遍历数组,同时维护指针两侧 1 的数量,可以达到 O ( n ) O(n) O ( n ) 
2022 年 12 月 01 日 
解题思路 
  先找出所有有效的点,然后取最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 impl  Solution {    pub  fn  nearest_valid_point i32 , y: i32 , points: Vec <Vec <i32 >>) -> i32  {         let  mut  valid = vec! [];         for  (index, xy) in  points.into_iter().enumerate() {             if  x == xy[0 ] || y == xy[1 ] {                 valid.push(((x - xy[0 ]).abs() + (y - xy[1 ]).abs(), index as  i32 ));             }         }         if  valid.is_empty() {             -1          } else  {             valid.into_iter().min().unwrap().1          }     } } 
2022 年 11 月 30 日 
解题思路 
  没思路,把题解翻译成 Rust 的交了(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 use  std::cmp;use  std::collections::HashMap;struct  FreqStack     freq: HashMap<i32 , i32 >,     group: HashMap<i32 , Vec <i32 >>,     max: i32  } impl  FreqStack {    fn  new Self  {         Self  {             freq: HashMap::new(),             group: HashMap::new(),             max: 0          }     }     fn  push mut  self , val: i32 ) {         self .freq.entry(val).and_modify(|it| *it += 1 ).or_insert(0 );         self .group.entry(self .freq[&val]).and_modify(|it| it.push(val)).or_insert(vec! [val]);         self .max = cmp::max(self .max, self .freq[&val]);     }     fn  pop mut  self ) -> i32  {         let  val = self .group.get_mut(&self .max).and_then(|it| it.pop()).unwrap();         self .freq.entry(val).and_modify(|it| *it -= 1 );         if  self .group[&self .max].is_empty() {             self .max -= 1 ;         }         val     } } 
2022 年 11 月 29 日 
解题思路 
  简单题,直接上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 use  std::cmp::min;impl  Solution {    pub  fn  min_operations str : String ) -> i32  {         let  (mut  a, mut  b) = (0 , 0 );         for  (i, ch) in  str .chars().enumerate() {             let  ch = ch as  usize  - '0'  as  usize ;             if  i & 1  != ch { a += 1  }             if  i & 1  == ch { b += 1  }         }         min(a, b)     } } 
2022 年 11 月 28 日 
解题思路 
  记忆化搜索,search(n, k) 表示前 n 个元素分成 k 个子数组的最大平均和,遍历分隔点,与 search(i, k - 1) 相加取最大值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 struct  Cache     memory: Vec <Vec <f64 >>, } impl  Cache {    fn  new usize ) -> Self  {         Self  {             memory: vec! [vec! [-1 .; n]; n],         }     }     fn  search mut  self , sums: &[f64 ], n: usize , k: usize ) -> f64  {         if  k == 1  {             return  sums[n] / n as  f64          }         match  self .memory[n][k] {             it if  it > 0 . => it,             _ => {                 let  mut  ans = -1 .;                 for  i in  k - 1  .. n {                     ans = f64 ::max(                         ans,                         self .search(sums, i, k - 1 ) + (sums[n] - sums[i]) / (n - i) as  f64 ,                     );                 }                 self .memory[n][k] = ans;                 ans             }         }     } } impl  Solution {    pub  fn  largest_sum_of_averages Vec <i32 >, k: i32 ) -> f64  {         let  (n, k) = (nums.len(), k as  usize );         let  mut  sums = vec! [0 .];         for  it in  nums {             sums.push(sums.last().unwrap() + it as  f64 );         }         Cache::new(n + 1 ).search(&sums, n, k)     } } 
2022 年 11 月 27 日 
解题思路 
  符合条件的数组在「循环轮转」过程中应至多只有一次下跌:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl  Solution {    pub  fn  check mut  nums: Vec <i32 >) -> bool  {         nums.push(nums[0 ]);         let  result =             nums.into_iter().fold(                 (0 , 0 ),                 |(pre, sum), it| {                     if  pre > it {                         (it, sum + 1 )                     } else  {                         (it, sum)                     }                 },             );         result.1  < 2      } } 
2022 年 11 月 26 日 
解题思路 
  细分后的节点数其实就是某种意义上的边权,可以在 Dijkstra 算法的基础之上加以改进来完成本题,具体操作见代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 use  std::cmp::Ordering;use  std::collections::{BinaryHeap, HashMap};#[derive(Eq)]   struct  Task     node: usize ,     distance: u32 , } impl  Ord  for  Task {      fn  cmp self , other: &Self ) -> Ordering {         other.distance.cmp(&self .distance)     } } impl  PartialOrd  for  Task {      fn  partial_cmp self , other: &Self ) -> Option <Ordering> {         Some (self .cmp(other))     } } impl  PartialEq  for  Task {      fn  eq self , other: &Self ) -> bool  {         self .distance == other.distance     } } impl  Task {    fn  new usize , distance: u32 ) -> Self  {         Self  { node, distance }     } } impl  Solution {    pub  fn  reachable_nodes Vec <Vec <i32 >>, max_moves: i32 , n: i32 ) -> i32  {         let  (n, max_moves) = (n as  usize , max_moves as  u32 );           let  mut  graph: HashMap<usize , HashMap<usize , u32 >> = HashMap::new();           for  next in  edges {               for  index in  0  ..= 1  {                 let  (src, dst) = (next[index] as  usize , next[1  - index] as  usize );                 let  distance = next[2 ] as  u32  + 1 ;                 graph                     .entry(src)                     .and_modify(|map| {                         map.insert(dst, distance);                     })                     .or_insert(HashMap::from([(dst, distance)]));             }         }         let  mut  distance = vec! [u32 ::MAX; n];         let  mut  visit = vec! [false ; n];         let  mut  queue: BinaryHeap<Task> = BinaryHeap::new();         distance[0 ] = 0 ;         queue.push(Task::new(0 , 0 ));         while  let  Some (Task { node: src, .. }) = queue.pop() {               if  visit[src] { continue ; }             visit[src] = true ;             if  !graph.contains_key(&src) {                 continue ;             }             for  (dst, range) in  &graph[&src] {                 let  new_distance = distance[src] + range;                 if  distance[*dst] > new_distance {                     distance[*dst] = new_distance;                     queue.push(Task::new(*dst, new_distance));                 }             }         }         let  mut  ans = 0 ;         for  it in  &mut  visit {             *it = false ;         }         for  (src, moves) in  distance.iter().enumerate() {             if  *moves > max_moves { continue ; }               ans += 1 ;               visit[src] = true ;             if  !graph.contains_key(&src) {                 continue ;             }             for  (dst, range) in  &graph[&src] {                   if  !visit[*dst] {                     ans += (max_moves.saturating_sub(*moves)                         + max_moves.saturating_sub(distance[*dst]))                     .min(*range - 1 );                 }             }         }         ans as  i32      } } 
2022 年 11 月 25 日 
解题思路 
  将所有单词拆分成「字母 - 长度」序列,例如 hello 可处理成 [(h, 1), (e, 1), (l, 2), (o, 1)],然后就可以很容易地判断每个输入是否满足条件。一个满足条件的单词 word 应符合:
  基于此,可以编写代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 impl  Solution {    fn  split str : String ) -> Vec <(char , usize )> {         let  mut  result: Vec <(char , usize )> = vec! [];         for  ch in  str .chars() {             match  result.last_mut() {                 Some (it) if  it.0  == ch => it.1  += 1 ,                 _ => result.push((ch, 1 )),             }         }         result     }     pub  fn  expressive_words str : String , words: Vec <String >) -> i32  {         let  str  = Self::split(str );         let  mut  ans = 0 ;         for  word in  words {             let  word = Self::split(word);             if  word.len() != str .len() {                 continue ;             }             if  str                  .iter()                 .zip(word)                 .all(|(x, y)| x.0  == y.0  && (x.1  == y.1  || x.1  >= 3 .max(y.1 )))             {                 ans += 1 ;             }         }         ans     } } 
2022 年 11 月 24 日 
解题思路 
  将数字分为大、中、小三类,其中「中数」指的是落在区间 [ L , R ] [L, R] [ L , R ] k k k k 2 + k 2 \frac{k^2 + k}{2} 2 k 2 + k  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 impl  Solution {    pub  fn  num_subarray_bounded_max mut  nums: Vec <i32 >, left: i32 , right: i32 ) -> i32  {         nums.push(i32 ::MAX);           let  mut  ans: i64  = 0 ;         let  (mut  mid, mut  min) = (0 , 0 );         for  i in  0  .. nums.len() {             if  nums[i] > right {                   let  n = (i - mid) as  i64 ;                 ans += (n * (n + 1 )) / 2 ;                 mid = i + 1 ;             }             if  nums[i] >= left {                   let  n = (i - min) as  i64 ;                 ans -= (n * (n + 1 )) / 2 ;                 min = i + 1 ;             }         }         ans as  i32      } } 
2022 年 11 月 23 日 
解题思路 
  模拟,按题目要求统计数量后取最大值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 use  std::collections::HashMap;impl  Solution {    pub  fn  count_balls i32 , high_limit: i32 ) -> i32  {         let  mut  map = HashMap::new();         for  mut  it in  low_limit ..= high_limit {             let  mut  sum = 0 ;             while  it != 0  {                 sum += it % 10 ;                 it /= 10 ;             }             if  let  Some (val) = map.remove(&sum) {                 map.insert(sum, val + 1 );             } else  {                 map.insert(sum, 1 );             }         }         *map.values().max().unwrap()     } } 
2022 年 11 月 22 日 
解题思路 
  设第 n 个「神奇数字」为 x,不难发现随着 n 的增长,x 的变化呈现周期性,其周期为:
T = lcm ( a , b ) a + lcm ( a , b ) b − 1 = a gcd ( a , b ) + b gcd ( a , b ) − 1 T = \frac{\text{lcm}(a, b)}{a} + \frac{\text{lcm}(a, b)}{b} - 1 = \frac{a}{\text{gcd}(a, b)} + \frac{b}{\text{gcd}(a, b)} - 1
 T = a lcm ( a , b )  + b lcm ( a , b )  − 1 = gcd ( a , b ) a  + gcd ( a , b ) b  − 1 
  T T T 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const  MOD: u64  = 1_000_000_007 ;fn  gcd u64 , b: u64 ) -> u64  {    if  a < b {         return  gcd(b, a);     }     if  b == 0  {         return  a;     }     gcd(b, a % b) } impl  Solution {    pub  fn  nth_magical_number i32 , a: i32 , b: i32 ) -> i32  {         let  (n, a, b) = (n as  u64 , a as  u64 , b as  u64 );         let  gcd = gcd(a, b);         let  lcm = a * b / gcd;         let  cycle = a / gcd + b / gcd - 1 ;         let  base = n / cycle * lcm % MOD;         let  (mut  last, mut  ax, mut  bx) = (0 , a, b);         for  _ in  0  .. n % cycle {             if  ax < bx {                 last = ax;                 ax += a;             } else  {                 last = bx;                 bx += b;             }         }         ((base + last) % MOD) as  _     } } 
  时间复杂度 O ( log  max ( a , b ) + max ( a , b ) ) O(\log \text{max}(a, b) + \text{max}(a, b)) O ( log  max ( a , b ) + max ( a , b )) 
2022 年 11 月 21 日 
解题思路 
  被题目的 n < 1e9 骗了,想了很久都没找到小于 O ( n 2 ) O(n^2) O ( n 2 ) 
  另外不难注意到,可以将 n 向上取整到 25 的倍数来进一步减小常数,最终只需要做一个 O ( − log  k ) O(- \log k) O ( − log  k ) k k k k = 1 0 − 5 k = 10^{-5} k = 1 0 − 5 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 use  std::collections::HashMap;#[derive(Default)] struct  Cache     cache: HashMap<(i32 , i32 ), f64 > } impl  Cache {    fn  search mut  self , a: i32 , b: i32 ) -> f64  {           if  self .cache.contains_key(&(a, b)) {             return  self .cache[&(a, b)];         }         let  result = match  (a, b) {             (a, b) if  (a <= 0  && b <= 0 ) => 0 . + 0.5 ,               (a, _) if  (a <= 0 ) => 1 . + 0 .,               (_, b) if  (b <= 0 ) => 0 . + 0 .,               (a, b) => {                 (0  .. 4 ).map(|i| self .search(a - 4  + i, b - i)).sum::<f64 >() * 0.25              }         };         self .cache.insert((a, b), result);         result     } } impl  Solution {    pub  fn  soup_servings mut  n: i32 ) -> f64  {         n = (n + 24 ) / 25 ;           if  n >= 200  {             return  1 .;         }         Cache::default().search(n, n)     } } 
2022 年 11 月 20 日 
解题思路 
  一杯一杯地倒和一次全倒完是一样的,我们可以把所有香槟都先倒在 [0, 0] 杯子里,然后向下处理溢出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 impl  Solution {    pub  fn  champagne_tower i32 , row: i32 , index: i32 ) -> f64  {         let  (poured, row, index) = (poured as  f64 , row as  usize , index as  usize );         let  mut  dp = vec! [vec! [0 .; row + 2 ]; row + 2 ];         dp[0 ][0 ] = poured;         for  i in  0  ..= row {             for  j in  0  ..= i {                 if  dp[i][j] > 1 . {                     let  overflow = (dp[i][j] - 1 .) / 2 .;                     dp[i + 1 ][j] += overflow;                     dp[i + 1 ][j + 1 ] += overflow;                     dp[i][j] = 1 .;                 }             }         }         dp[row][index]     } } 
  时间和空间都还能做一些优化,懒得搞了。以及似乎有数学解法(?
2022 年 11 月 19 日 
解题思路 
  求前缀和,然后找最大值即可
1 2 3 4 5 6 7 8 9 10 11 impl  Solution {    pub  fn  largest_altitude Vec <i32 >) -> i32  {         let  mut  alt = vec! [0 ];         for  it in  gain {             alt.push(alt.last().unwrap() + it);         }         *alt.iter().max().unwrap()     } } 
2022 年 11 月 18 日 
解题思路 
  考虑一段跨度为 k k k k − 1 k-1 k − 1 S S S 
W = ( max  ( S ) − min  ( S ) ) ⋅ 2 k − 2 W = (\max(S) - \min(S)) \cdot 2^{k - 2}
 W = ( max ( S ) − min ( S )) ⋅ 2 k − 2 
  其中 max  ( S ) − min  ( S ) \max(S) - \min(S) max ( S ) − min ( S ) 2 k − 2 2^{k - 2} 2 k − 2 k k k 
W ∑ = ∑ i = 1 N − 1 ∑ j = 0 i − 1 ( a i − a j ) ⋅ 2 i − j − 1 = ∑ i = 1 N − 1 ( a i ⋅ ∑ j = 0 i − 1 2 i − j − 1 − ∑ j = 0 i − 1 a j ⋅ 2 i − j − 1 ) = ∑ i = 1 N − 1 a i ⋅ ( 2 i − 1 ) − ∑ i = 1 N − 1 ∑ j = 0 i − 1 a j ⋅ 2 i − j − 1 = ∑ i = 1 N − 1 a i ⋅ ( 2 i − 1 ) − ∑ j = 0 N − 2 a j ⋅ ∑ i = j + 1 N − 1 2 i − j − 1 = ∑ i = 1 N − 1 a i ⋅ ( 2 i − 1 ) − ∑ j = 0 N − 2 a j ⋅ ( 2 N − j − 1 − 1 ) = ∑ k = 1 N − 1 [ a k ⋅ ( 2 k − 1 ) − a k − 1 ⋅ ( 2 N − k − 1 ) ] \begin{align*}
W_{\sum} &= \sum_{i = 1}^{N - 1} \sum_{j = 0}^{i - 1} (a_i - a_j) \cdot 2^{i - j - 1} \\
  &= \sum_{i = 1}^{N - 1}(a_i \cdot \sum_{j = 0}^{i - 1} 2^{i - j - 1} - \sum_{j = 0}^{i - 1} a_j \cdot 2^{i - j - 1}) \\
  &= \sum_{i = 1}^{N - 1} a_i \cdot (2^i - 1) - \sum_{i = 1}^{N - 1} \sum_{j = 0}^{i - 1} a_j \cdot 2^{i - j - 1} \\
  &= \sum_{i = 1}^{N - 1} a_i \cdot (2^i - 1) - \sum_{j = 0}^{N - 2} a_j \cdot \sum_{i = j + 1}^{N - 1} 2^{i - j - 1} \\
  &= \sum_{i = 1}^{N - 1} a_i \cdot (2^i - 1) - \sum_{j = 0}^{N - 2} a_j \cdot (2^{N - j - 1} - 1) \\
  &= \sum_{k = 1}^{N - 1} \left[ a_k \cdot (2^k - 1) - a_{k - 1} \cdot (2^{N - k} - 1) \right]
\end{align*}
 W ∑   = i = 1 ∑ N − 1  j = 0 ∑ i − 1  ( a i  − a j  ) ⋅ 2 i − j − 1 = i = 1 ∑ N − 1  ( a i  ⋅ j = 0 ∑ i − 1  2 i − j − 1 − j = 0 ∑ i − 1  a j  ⋅ 2 i − j − 1 ) = i = 1 ∑ N − 1  a i  ⋅ ( 2 i − 1 ) − i = 1 ∑ N − 1  j = 0 ∑ i − 1  a j  ⋅ 2 i − j − 1 = i = 1 ∑ N − 1  a i  ⋅ ( 2 i − 1 ) − j = 0 ∑ N − 2  a j  ⋅ i = j + 1 ∑ N − 1  2 i − j − 1 = i = 1 ∑ N − 1  a i  ⋅ ( 2 i − 1 ) − j = 0 ∑ N − 2  a j  ⋅ ( 2 N − j − 1 − 1 ) = k = 1 ∑ N − 1  [ a k  ⋅ ( 2 k − 1 ) − a k − 1  ⋅ ( 2 N − k − 1 ) ]  
  利用等比数列的求和公式,我们巧妙地把 O ( n 2 ) O(n^2) O ( n 2 ) O ( n ) O(n) O ( n ) 2 k 2^k 2 k O ( n ) O(n) O ( n ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const  MOD: u64  = 1000000007 ;impl  Solution {    pub  fn  sum_subseq_widths Vec <i32 >) -> i32  {         let  n = nums.len();         let  mut  nums: Vec <_> = nums.into_iter().map(|it| it as  u64 ).collect();         nums.sort();         let  mut  ans = 0 ;         let  mut  pow_k = vec! [1 ];         for  i in  1  .. n {             pow_k.push(pow_k[i - 1 ] * 2  % MOD);         }         for  i in  1  .. n {             ans += (nums[i] * (pow_k[i] - 1 )) % MOD;             ans += MOD - (nums[i - 1 ] * (pow_k[n - i] - 1 )) % MOD;             ans %= MOD;         }         ans as  _     } } 
2022 年 11 月 17 日 
解题思路 
  硬生生把中等题写成了困难题 
  对 words 建立字典树,然后维护一个 next: Map<char, Vec<Trie>> 映射,表示从当前状态出发,接收一个字符 char 能够访问到的节点集合,然后对于输入序列,不断更新 next 集并统计答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 use  std::str ::Chars;macro_rules!  ord {    ($ch: expr) => {         ($ch as  usize  - 'a'  as  usize )     }; } macro_rules!  chr {    ($id: expr) => {         ((($id as  u8 ) + b'a' ) as  char )     }; } #[derive(PartialEq, Eq, Hash, Default)] struct  Trie     mark: i32 ,     next: [Option <Box <Trie>>; 26 ], } impl  Trie {      fn  insert mut  self , mut  chars: Chars) {         if  let  Some (ch) = chars.next() {             let  index = ord!(ch);             if  matches!(self .next[index], None ) {                 self .next[index] = Some (Box ::new(Trie::default()))             }             self .next[index].as_mut().unwrap().insert(chars);         } else  {             self .mark += 1 ;         }     }     fn  nexts self ) -> Vec <(char , &Trie)> {           self .next             .iter()             .enumerate()             .filter_map(|(ch, node)| node.as_ref().map(|it| (chr!(ch), it.as_ref())))             .collect()     } } impl  Solution {    fn  build Vec <String >) -> Trie {           let  mut  root = Trie::default();         for  word in  words {             root.insert(word.chars().into_iter());         }         root     }     pub  fn  num_matching_subseq String , words: Vec <String >) -> i32  {         let  root = Self::build(words);         let  mut  next: [Vec <_>; 26 ] = Default ::default();         let  mut  ans = 0 ;         for  (ch, node) in  root.nexts() {             next[ord!(ch)].push(node);         }         for  ch in  seq.chars() {             for  node in  next[ord!(ch)].split_off(0 ) {                 ans += node.mark;                   for  (ch, node) in  node.nexts() {                     next[ord!(ch)].push(node);                 }             }         }         ans     } } 
  上面的代码偏工程性,且尚有可优化之处(例如 Trie::nexts() 可使用更高效的数据结构维护指向下一级的指针,需要时直接返回迭代器,避免遍历全部 26 个字母)
2022 年 11 月 16 日 
解题思路 
  乍一看像是求逆序对的题,但是题目只要求我们输出 true 或 false,并不需要求出具体数量,这就和 2022 年 10 月 10 日题目中「数组切分」的思想很相似了,只有数组中所有元素偏离原位置(排序后的位置)不超过 1,才可能满足题目条件:
1 2 3 4 5 6 7 8 9 10 11 impl  Solution {    pub  fn  is_ideal_permutation Vec <i32 >) -> bool  {         for  (i, x) in  nums.into_iter().enumerate() {             if  (i as  i32  - x).abs() > 1  {                 return  false ;             }         }         true      } } 
2022 年 11 月 15 日 
解题思路 
  贪心求解,尽可能选择单元数更多的箱子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 impl  Solution {    pub  fn  maximum_units mut  box_types: Vec <Vec <i32 >>, mut  truck_size: i32 ) -> i32  {         box_types.sort_by_key(|it| -it[1 ]);         let  mut  ans = 0 ;         for  it in  box_types {             if  truck_size > it[0 ] {                 ans += it[0 ] * it[1 ];                 truck_size -= it[0 ];             } else  {                 ans += truck_size * it[1 ];                 break ;             }         }         ans     } } 
2022 年 11 月 14 日 
解题思路 
  dp 确实不咋会做(
  看了题解说还可以折半搜索,重新捋一捋思路🤔。设总体和为 S S S N N N N 1 N_1 N 1  N 2 N_2 N 2  S 1 S_1 S 1  S 2 S_2 S 2  
S 1 N 1 = S 2 N 2 S 1 N 2 = S 2 N 1 S 1 N 2 + S 1 N 1 = S 2 N 1 + S 1 N 1 S 1 ( N 1 + N 2 ) = ( S 1 + S 2 ) N 1 S 1 N = S N 1 S 1 N 1 = S N \begin{align*}
\frac{S_1}{N_1} &= \frac{S_2}{N_2} \\
S_1 N_2 &= S_2 N_1 \\
S_1 N_2 + S_1 N_1 &= S_2 N_1 + S_1 N_1 \\
S_1 (N_1 + N_2) &= (S_1 + S_2) N_1 \\
S_1 N &= S N_1 \\
\frac{S_1}{N_1} &= \frac{S}{N}
\end{align*}
 N 1  S 1   S 1  N 2  S 1  N 2  + S 1  N 1  S 1  ( N 1  + N 2  ) S 1  N N 1  S 1    = N 2  S 2   = S 2  N 1  = S 2  N 1  + S 1  N 1  = ( S 1  + S 2  ) N 1  = S N 1  = N S   
  也就是说,当数组的某一部分均值与整体均值相等的时候,一定能划分为满足条件的两组。但如果我们如此暴力枚举所有可能的组合,即使只考虑 N 1 < N 2 N_1 < \frac{N}{2} N 1  < 2 N  2 N − 1 = 2 29 2^{N-1} = 2^{29} 2 N − 1 = 2 29 
  但是转念一想,这不就是 两数之和  嘛!我们可以将数组平均划分为 S L S_L S L  S R S_R S R  N 2 \frac{N}{2} 2 N  S R S_R S R  N R ′ N_R' N R ′  S R ′ S_R' S R ′  S N R ′ − N S R ′ SN_R' - NS_R' S N R ′  − N S R ′  S L S_L S L  
S L ′ + S R ′ N L ′ + N R ′ = S N N S L ′ + N S R ′ = S N L ′ + S N R ′ N S L ′ − S N L ′ = S N R ′ − N S R ′ \frac{S_L' + S_R'}{N_L' + N_R'} = \frac{S}{N} \\ \quad \\
\begin{align*}
N S_L' + N S_R' &= S N_L' + S N_R' \\
N S_L' - S N_L' &= S N_R' - N S_R'
\end{align*}
 N L ′  + N R ′  S L ′  + S R ′   = N S  N S L ′  + N S R ′  N S L ′  − S N L ′   = S N L ′  + S N R ′  = S N R ′  − N S R ′   
  借助 Hash 表,我们便可以在 O ( 1 ) O(1) O ( 1 ) N L ′ N_L' N L ′  S L ′ S_L' S L ′  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 use  std::collections::HashMap;impl  Solution {    pub  fn  split_array_same_average Vec <i32 >) -> bool  {         let  (n, s) = (nums.len(), nums.iter().sum::<i32 >());         let  (left, right) = (&nums[..n / 2 ], &nums[n / 2 ..]);         if  n == 1  {               return  false ;         }         let  mut  dict: HashMap<_, _> = HashMap::new();         for  x in  1 ..1  << right.len() {             let  (mut  sp, mut  np) = (0 , 0 );             for  i in  0 ..right.len() {                 if  (1  << i) & x != 0  {                     sp += right[i];                     np += 1 ;                 }             }             let  magic = s * np - n as  i32  * sp;             if  magic == 0  {                   return  true ;             }             dict.insert(magic, np);         }         for  x in  1 ..1  << left.len() {             let  (mut  sp, mut  np) = (0 , 0 );             for  i in  0 ..left.len() {                 if  (1  << i) & x != 0  {                     sp += left[i];                     np += 1 ;                 }             }             let  magic = n as  i32  * sp - s * np;             if  magic == 0  {                   return  true ;             }             if  let  Some (nr) = dict.get(&magic) {                   if  np + nr != n as  i32  {                     return  true ;                 }             }         }         false      } } 
  此题各种边界情况比较多,需要注意特判,此外上面的代码比较混乱,我们可以整理一下代码使其更加优雅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 use  std::collections::HashMap;impl  Solution {    pub  fn  split_array_same_average mut  nums: Vec <i32 >) -> bool  {         let  (n, s) = (nums.len(), nums.iter().sum::<i32 >());         let  right = nums.split_off(n / 2 );         if  n == 1  {               return  false ;         }         let  mut  dict: HashMap<_, _> = HashMap::new();         let  select = |arr: Vec <i32 >| {             (1  .. 1  << arr.len()).map(move  |x| {                 let  (mut  sp, mut  np) = (0 , 0 );                 for  i in  0  .. arr.len() {                     if  (1  << i) & x != 0  {                         sp += arr[i];                         np += 1 ;                     }                 }                 (s * np - n as  i32  * sp, np)             })         };         for  (magic, np) in  select(nums) {             if  magic == 0  {                   return  true ;             }             dict.insert(magic, np);         }         for  (magic, np) in  select(right) {             if  magic == 0  {                   return  true ;             }             match  dict.get(&-magic) {                   Some (nr) if  np + nr != n as  i32  => return  true ,                 _ => ()             }         }         false      } } 
  折半搜索,时间复杂度 O ( ( 2 ) N ) O((\sqrt{2})^N) O (( 2  ) N ) 
2022 年 11 月 13 日 
  锵锵复活~
  电脑还没修好,但我不能再坐以待毙了!于是把笔记本的 Windows 扬了,腾出 1T 的空闲来,把台式机的硬盘整个 dd 进去,几番折腾终于秽土转生……
解题思路 
  简单排序题,提前规划好 index 可以加速计算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 impl  Solution {    #[inline]      fn  ord char ) -> usize  {         ch as  usize  - 'a'  as  usize      }     pub  fn  custom_sort_string String , s: String ) -> String  {         let  mut  chars: Vec <_> = s.chars().collect();         let  mut  index = vec! [0 ; 26 ];         for  (i, ch) in  order.chars().enumerate() {             index[Self::ord(ch)] = i;         }         chars.sort_by_key(|it| index[Self::ord(*it)]);         chars.into_iter().collect()     } } 
寄! 
  电脑寄了(悲
  维修师傅让把 CPU 和主板寄回去返厂,结果到了让技术员测试又说没有任何问题,几番沟通之后现在又怀疑是显卡的问题,害……
2022 年 11 月 01 日 
解题思路 
  这真的有什么好解释的么🤔
1 2 3 4 5 impl  Solution {    pub  fn  array_strings_are_equal Vec <String >, word2: Vec <String >) -> bool  {         word1.join("" ) == word2.join("" )     } } 
2022 年 10 月 31 日 
解题思路 
  模拟嘛,只要按照题目的意思构造一个序列出来就行了。用一个 index 记录当前规则(放几个),然后循环向数组里 push:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl  Solution {    pub  fn  magical_string i32 ) -> i32  {         let  mut  arr = vec! [1 ];         let  mut  index = 1 ;         while  arr.len() < n as  _ {             let  what = 3  - arr.last().unwrap();             arr.push(what);               if  arr[index] == 2  { arr.push(what); }               index += 1 ;         }         arr.into_iter().take(n as  _).filter(|it| *it == 1 ).sum()     } } 
  除了打表之外貌似没有时间复杂度小于 O ( n ) O(n) O ( n ) 
2022 年 10 月 30 日 
解题思路 
  其实关键并不在于如何 AC,而是如何优雅地 AC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 impl  Solution {    pub  fn  letter_case_permutation String ) -> Vec <String > {         let  chars: Vec <_> = s.to_lowercase().chars().collect();         let  mut  mask = 1 ;         let  index: Vec <_> = chars.iter().map(|ch| {             if  ch.is_ascii_digit() {                 0              } else  {                 let  backup = mask;                 mask <<= 1 ;                 backup             }         }).collect();         let  mut  ans = vec! [];         for  i in  0  .. mask {             ans.push(                 chars.iter().zip(index.iter()).map(|(ch, mask)| {                     if  mask & i != 0  {                         ((*ch as  u8 ) - 32 ) as  char                      } else  {                         *ch                     }                 }).collect()             );         }         ans     } } 
2022 年 10 月 29 日 
解题思路 
  十分适合新手的一道题,练习 match 的使用(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 impl  Solution {    pub  fn  count_matches Vec <Vec <String >>, rule_key: String , rule_value: String ) -> i32  {         let  mut  ans = 0 ;         for  item in  items {             ans += match  rule_key.as_str() {                 "type"  => item[0 ] == rule_value,                 "color"  => item[1 ] == rule_value,                 "name"  => item[2 ] == rule_value,                 _ => panic! ()             } as  i32 ;         }         ans     } } 
2022 年 10 月 22 日 ~ 2022 年 10 月 28 日 
  一年一度的 Hackergame 开赛,我就不一心二用了,比赛十分精彩,欢迎各位前来体验!
2022 年 10 月 21 日 
解题思路 
  记录每个位置向前最大跨度的下标,这样后续搜索时就能够跳过这些位置,从而实现加速:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct  StockSpanner     price: Vec <i32 >,     prev: Vec <usize >, } impl  StockSpanner {    fn  new Self  {         Self  {             price: vec! [],             prev: vec! [],         }     }     fn  next mut  self , price: i32 ) -> i32  {         self .price.push(price);         let  length = self .prev.len();         let  mut  index = length;         while  index >= 1  && self .price[length] >= self .price[index - 1 ] {             index = self .prev[index - 1 ];         }         self .prev.push(index);         (length - index + 1 ) as  _     } } 
  复杂度似乎是 O ( n ) O(n) O ( n ) 
2022 年 10 月 20 日 
解题思路 
  用数组 F[i, j] 表示第 n - 1 行,下标 k - 1 的元素,不难得到递推表达式为 F[0, 0] = 0、F[i, j] = F[i - 1, j >> 1] ^ (j & 1),将其表示为递归形式即可。时间复杂度 O ( log  k ) O(\log k) O ( log  k ) 
  找规律(不会证😭),可知 k - 1 的二进制表示中 1 的数量就是答案:
1 2 3 4 5 6 7 8 9 10 11 impl  Solution {    pub  fn  kth_grammar i32 , mut  k: i32 ) -> i32  {         let  mut  ans = 0 ;         k -= 1 ;         while  k != 0  {             k ^= (k & -k);             ans ^= 1 ;         }         ans     } } 
  最坏情况下,时间复杂度 O ( log  k ) O(\log k) O ( log  k ) O ( 1 ) O(1) O ( 1 ) 
2022 年 10 月 19 日 
解题思路 
  统计出两种学生的数量,然后扫描三明治数组,当有三明治无法被消耗时退出循环,或完成遍历后返回 0(所有人都有得吃)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 impl  Solution {    pub  fn  count_students Vec <i32 >, sandwiches: Vec <i32 >) -> i32  {         let  mut  count = [0 ; 2 ];         count[0 ] = students.iter().filter(|it| **it == 0 ).count();         count[1 ] = students.iter().filter(|it| **it == 1 ).count();         for  (i, it) in  sandwiches.iter().enumerate() {             let  it = *it as  usize ;             if  count[it] > 0  {                 count[it] -= 1 ;             } else  {                 return  (students.len() - i) as  _;             }         }         0      } } 
  昨天忘记更新博客了 qwq
2022 年 10 月 18 日 
解题思路 
  样例 2 已经在疯狂暗示,在一定范围内这个问题只是简单的等比求和,所以我们只需要特殊处理最后非等比的一小段即可。用数组 dp[i][j] 表示无 n 限制下,长度为 i,最高位小于等于 j 时解的数量,可以将问题分解为「比 n 位数少的」和「与 n 位数相同的」求解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 use  std::collections::HashSet;impl  Solution {    pub  fn  at_most_n_given_digit_set Vec <String >, mut  n: i32 ) -> i32  {                  let  digits: HashSet<_> = digits.iter().map(|it| it.as_bytes()[0 ] as  usize  - 0x30 ).collect();                  let  split = {             let  mut  tmp = vec! [];             while  n != 0  {                 tmp.insert(0 , n as  usize  % 10 );                 n /= 10 ;             }             tmp         };         let  length = split.len();                  let  mut  dp = [[0 ; 10 ]; 11 ];         for  i in  1  ..= length {             for  j in  1  ..= 9  {                 if  i == 1  {                     dp[i][j] = dp[i][j - 1 ] + if  digits.contains(&j) { 1  } else  { 0  };                 } else  {                     dp[i][j] = dp[i - 1 ][j] * digits.len();                 }             }         }         let  mut  ans = 0 ;                  for  row in  &dp[1  .. length] {             ans += row[9 ];         }                  for  i in  0  .. length {             let  x = split[i];             ans += dp[length - i][x.saturating_sub(1 )];             if  i == length - 1  && digits.contains(&x) {                 ans += 1 ;             }             if  !digits.contains(&x) {                 break ;             }         }         ans as  _     } } 
2022 年 10 月 17 日 
解题思路 
  滑动窗口的思想,用一个双向队列实现,循环中每轮先摘水果 push 到队尾,然后检查种类数是否满足要求,如果不满足就一直从队头 pop,直到满足要求为止,循环过程中记录一下滑动窗口的最大长度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 use  std::cmp;use  std::collections::VecDeque;struct  Basket Vec <(i32 , usize )>);impl  Basket {    fn  new Self  {         Self (vec! [])     }     fn  types self ) -> usize  {         self .0 .len()     }     fn  add mut  self , key: i32 ) {         for  pair in  &mut  self .0  {             if  pair.0  == key {                 pair.1  += 1 ;                 return ;             }         }         self .0 .push((key, 0 ));     }     fn  remove mut  self , key: i32 ) {         let  mut  index = usize ::MAX;         for  (i, pair) in  self .0 .iter_mut().enumerate() {             if  pair.0  == key {                 if  pair.1  > 0  {                     pair.1  -= 1 ;                     return ;                 } else  {                     index = i;                     break ;                 }             }         }         self .0 .remove(index);     } } impl  Solution {    pub  fn  total_fruit Vec <i32 >) -> i32  {         let  mut  window = VecDeque::new();         let  mut  basket = Basket::new();         let  mut  ans = 0 ;         for  color in  fruits {             window.push_back(color);             basket.add(color);             while  basket.types() > 2  {                   basket.remove(window.pop_front().unwrap());             }             ans = cmp::max(ans, window.len());         }         ans as  _     } } 
2022 年 10 月 16 日 
解题思路 
  将所有人分为 3 个集合:S、T 和 None,初始所有人均位于 None 集中。考虑广度优先搜索,我们将一号节点加入待处理队列和 S 集,每次从待处理队列中取出一个节点,检查他与仇敌是否在同一个集合,是则直接返回 false,否则将仇敌加入对立集合和处理队列
  注意考虑节点之间没有任何直接或间接联系时的处理方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 use  std::collections::{BTreeMap, VecDeque};impl  Solution {    pub  fn  possible_bipartition i32 , dislikes: Vec <Vec <i32 >>) -> bool  {         let  mut  group = vec! [0 ; n as  _];           let  dislikes = {               let  mut  tmp: BTreeMap<usize , Vec <usize >> = BTreeMap::new();             for  pair in  dislikes {                 let  mut  pair: Vec <_> = pair.iter().map(|it| *it as  usize  - 1 ).collect();                 for  _ in  0  .. 2  {                     if  let  Some (list) = tmp.get_mut(&pair[0 ]) {                         list.push(pair[1 ]);                     } else  {                         tmp.insert(pair[0 ], vec! [pair[1 ]]);                     }                     pair.reverse();                 }             }             tmp         };         for  i in  0  .. n as  _ {             if  group[i] != 0  { continue ; }               let  mut  task = VecDeque::from([i]);             group[i] = 1 ;               while  let  Some (now) = task.pop_front() {                 if  let  Some (opponents) = dislikes.get(&now) {                     for  op in  opponents {                         if  group[*op] == group[now] { return  false ; }                           if  group[*op] == 0  {                             group[*op] = -group[now];                               task.push_back(*op);                           }                     }                 }             }         }         true      } } 
建图的时候直接 BTreeMap 摆烂了,用前向星存图可以更快 
 
2022 年 10 月 15 日 
解题思路 
  基本思想:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 impl  Solution {    pub  fn  build_array Vec <i32 >, n: i32 ) -> Vec <String > {         let  mut  size = 0 ;         let  mut  output = Vec ::new();         for  i in  1  ..= n {             output.push(String ::from("Push" ));                          if  target[size] != i {                 output.push(String ::from("Pop" ));             } else  {                 size += 1 ;             }                          if  size == target.len() {                 break ;             }         }         output     } } 
2022 年 10 月 14 日 
解题思路 
朴素算法 
  看到取模 1e9 + 7 直接一眼定真:动态规划,然后来想想状态如何建立🤔。不妨用状态数组 d p c h , j dp_{ch,j} d p c h , j  c h ch c h j + 1 j + 1 j + 1 
d p c h , j = { 1 j = 0 ∑ d p ∗ , j − 1 j > 0 dp_{ch, j} = \begin{cases}
1 & j = 0 \\
\sum dp_{*, j - 1} & j > 0
\end{cases}
 d p c h , j  = { 1 ∑ d p ∗ , j − 1   j = 0 j > 0  
  当长度为 1 时,字序列就是字符 c h ch c h j j j c h ch c h d p dp d p d p dp d p abca,有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # 初始化所有元素为 0(未考虑到的部分用「-」标记)  dp | 0 1 2 3 ----+--------- 'a' | - - - - 'b' | - - - - 'c' | - - - - 1. 处理字符 'a' 后  dp | 0 1 2 3 ----+--------- 'a' | 1 - - - 'b' | 0 - - - 'c' | 0 - - - 2. 处理字符 'c' 后  dp | 0 1 2 3 ----+--------- 'a' | 1 0 - -  'b' | 0 0 - - 'c' | 1 1 - - 3. 处理字符 'b' 后  dp | 0 1 2 3 ----+--------- 'a' | 1 0 0 - 'b' | 1 2 1 - 'c' | 1 1 0 - 4. 处理字符 'c' 后  dp | 0 1 2 3 ----+--------- 'a' | 1 0 0 0 'b' | 1 2 1 0 'c' | 1 3 3 1 
  最后把数组元素全部加起来(注意取模),就是最终的答案,下面是 rust 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 impl  Solution {    pub  fn  distinct_subseq_ii String ) -> i32  {         const  MOD: usize  = 1_000_000_007 ;         const  LETTERS: usize  = 26 ;         let  n = s.len();         let  s: Vec <_> = s.chars().collect();         let  mut  ans = 0 ;         let  mut  dp = vec! [vec! [0 ; n]; LETTERS];         for  i in  0  .. n {             let  ch = s[i] as  usize  - 'a'  as  usize ;             for  j in  (0  ..= i).rev() {                 dp[ch][j] = if  j == 0  {                     1                  } else  {                     let  mut  sum = 0 ;                     for  k in  0  .. LETTERS {                         sum = (sum + dp[k][j - 1 ]) % MOD;                     }                     sum                 }             }         }         for  i in  0  .. n {             for  j in  0  .. LETTERS {                 ans = (ans + dp[j][i]) % MOD;             }         }         ans as  i32      } } 
  朴素算法的时间复杂度为 O ( n 2 m ) O(n^2m) O ( n 2 m ) m m m 
性能调优 
  再一看,似乎没有必要每次都重新计算每列的总和,考虑引入一个数组 sum,动态地维护每次变化的量,可以将时间复杂度优化到 O ( n 2 ) O(n^2) O ( n 2 ) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 impl  Solution {    pub  fn  distinct_subseq_ii String ) -> i32  {         const  MOD: usize  = 1_000_000_007 ;         let  n = s.len();         let  s: Vec <_> = s.chars().collect();         let  mut  ans = 0 ;         let  mut  dp = vec! [vec! [0 ; n]; 26 ];         let  mut  sum = vec! [0 ; n];         for  i in  0  .. n {             let  ch = s[i] as  usize  - 'a'  as  usize ;             for  j in  (0  ..= i).rev() {                 let  new = if  j == 0  {                     1                  } else  {                     sum[j - 1 ]                 };                 sum[j] = (sum[j] + MOD + new - dp[ch][j]) % MOD;                 dp[ch][j] = new;             }         }         for  i in  0  .. n {             ans = (ans + sum[i]) % MOD;         }         ans as  i32      } } 
  能 AC 了,但总感觉还是不够优雅。
极致优化 
  这一次尝试再将数组 d p dp d p d p c h dp_{ch} d p c h  c h ch c h c h ch c h d p c h dp_{ch} d p c h  ∑ d p + 1 \sum dp + 1 ∑ d p + 1 acbc 举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 初始化为全 0 dp = [0, 0, 0] 1. 处理字符 'a' 后 dp = [1, 0, 0] 2. 处理字符 'c' 后 dp = [1, 0, 2] 3. 处理字符 'b' 后 dp = [1, 4, 2] 4. 处理字符 'c' 后 dp = [1, 4, 8] 
  在遇到第二个字符 c 时,d p dp d p *a、*b、*c 的数量,将字符 c 加在各字序列末尾,得到 *ac、*bc、*cc,又由于它们 * 部分各不相同,所以不会产生重复,最后再加上一个字符 c 本身,就是新的 d p c dp_c d p c  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl  Solution {    pub  fn  distinct_subseq_ii String ) -> i32  {         const  MOD: usize  = 1_000_000_007 ;         let  mut  dp = vec! [0 ; 26 ];         let  mut  sum = 0 ;         for  ch in  s.chars() {             let  ch = ch as  usize  - 'a'  as  usize ;             let  old = dp[ch];             dp[ch] = sum + 1 ;             sum = (sum + MOD - old + dp[ch]) % MOD;         }         sum as  i32      } } 
  最终时间复杂度 O ( n ) O(n) O ( n ) O ( m ) O(m) O ( m ) 
2022 年 10 月 13 日 
解题思路 
朴素算法 
  对于 arr[i] = k,若要满足题目要求,则至少区间 [min(i, k), max(i, k)] 会落在同一个分区中。那么只需遍历 arr,对每一个区间做并查集,统计最终的集合数量即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 use  std::cmp::{max, min};impl  Solution {    pub  fn  max_chunks_to_sorted Vec <i32 >) -> i32  {         let  n = arr.len();         let  mut  dsu: Vec <_> = (0  .. n).collect();         for  i in  0  .. n {             let  (l, r) = (min(i, arr[i] as  usize ), max(i, arr[i] as  usize ));             for  j in  l .. r {                 dsu[j] = r;             }         }         let  mut  ans = 0 ;         for  i in  0  .. n {             if  dsu[i] == i { ans += 1 ; }         }         ans     } } 
性能调优 
  写完之后打眼一看,似乎并没有用到并查集「查」的功能;又由于 arr 中的元素有且仅有 [1, n) ,一个萝卜一个坑,错位的元素必然会组成循环,所以我们只需要统计 k > i 的情况。那么可以将代码优化成下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 use  std::cmp::max;impl  Solution {    pub  fn  max_chunks_to_sorted Vec <i32 >) -> i32  {         let  (n, mut  ans) = (arr.len(), 0 );         let  mut  sign = 0 ;         for  i in  0  .. n {             sign = max(sign, arr[i] as  usize );               if  sign == i { ans += 1 ; }           }         ans     } } 
  最终时间复杂度 O ( n ) O(n) O ( n ) O ( 1 ) O(1) O ( 1 ) n <= 10
2022 年 10 月 12 日 
解题思路 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 use  std::collections::BTreeSet;impl  Solution {    pub  fn  num_components Option <Box <ListNode>>, nums: Vec <i32 >) -> i32  {                  let  mut  head = &head;         let  nums: BTreeSet<_> = nums.iter().collect();         let  mut  ans = 0 ;         let  mut  last = false ;           while  let  Some (node) = head {             let  now = nums.contains(&node.val);             if  now && !last {                 ans += 1 ;             }             last = now;             head = &node.next;         }         if  last { ans += 1 ; }           ans     } } 
  时间复杂度 O ( n log  n ) O(n \log n) O ( n log  n ) O ( n ) O(n) O ( n ) 
2022 年 10 月 11 日 
解题思路 
  三个 if,让 OJ 为我通过 18 个测试点: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl  Solution {    pub  fn  are_almost_equal String , s2: String ) -> bool  {         if  s1 == s2 { return  true ; }           let  a1: Vec <_> = s1.chars().collect();         let  a2: Vec <_> = s2.chars().collect();         let  mut  diff = Vec ::new();         for  i in  0  .. a1.len() {             if  a1[i] != a2[i] { diff.push(i); }         }         if  diff.len() != 2  { return  false ; }           a1[diff[0 ]] == a2[diff[1 ]] && a2[diff[0 ]] == a1[diff[1 ]]       } } 
  时间复杂度 O ( n ) O(n) O ( n ) O ( n ) O(n) O ( n ) 
2022 年 10 月 10 日 
解题思路 
  拿到题目先贪它一把,从数组开头往后捋,一旦遇到违反递增规则的数就交换,记录交换的次数。同时不难注意到,交换 k k k N − k N - k N − k N N N 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 use  std::cmp::min;use  std::mem::swap;impl  Solution {    pub  fn  min_swap mut  nums1: Vec <i32 >, mut  nums2: Vec <i32 >) -> i32  {         let  n = nums1.len();         let  mut  ans: usize  = 0 ;         for  i in  1  .. n {             if  nums1[i - 1 ] >= nums1[i] || nums2[i - 1 ] >= nums2[i] {                 swap(&mut  nums1[i], &mut  nums2[i]);                 ans += 1 ;             }         }         min(ans, n - ans) as  i32      } } 
  然后 …… 意料之中的 WA 了,来看看导致错误的这组输入:
1 2 nums1 = [0 4 4 5 9] nums2 = [0 1 6 8 10] 
  按照我们的逻辑,将在 index 为 2 和 3 的时候对数组进行翻转,此时 ans 等于 2,返回的 min(ans, n - ans) 依然为 2 …… 问题出现了,翻转所有其它元素并不比翻转 (4, 6)、(5, 8) 两组的「开销」更低
  注意到,在上述情况中,「翻转所有其它元素」其实并没有必要包含 (0, 0) 和 (9, 10) 这两对,因为它们无论如何翻转,都不会影响最终「严格递增」这一要求。所以不妨考虑首先将 nums1 和 nums2 进行分段,如果 max(nums1[i - 1], nums2[i - 1]) < min(nums1[i], nums2[i]),则进行一次切分。处理后的两个数组:
1 2 nums1 = [0 | 4 4 5 | 9] nums2 = [0 | 1 6 8 | 10] 
  切分出来的三段相互独立、互不影响,只需要对每一段都跑一次上面的贪心方法,然后将最终结果累加起来即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 use  std::cmp::{max, min};use  std::mem::swap;impl  Solution {    pub  fn  min_swap mut  nums1: Vec <i32 >, mut  nums2: Vec <i32 >) -> i32  {                  nums1.push(i32 ::MAX); nums2.push(i32 ::MAX);         let  n = nums1.len();         let  mut  ans: usize  = 0 ;         let  mut  index = 0 ;           for  i in  1  .. n {             if  max(nums1[i - 1 ], nums2[i - 1 ]) < min(nums1[i], nums2[i]) {                 let  mut  result = 0 ;                   for  j in  index .. i - 1  {                       if  nums1[j] >= nums1[j + 1 ] || nums2[j] >= nums2[j + 1 ] {                         swap(&mut  nums1[j + 1 ], &mut  nums2[j + 1 ]);                         result += 1 ;                     }                 }                 ans += min(result, i - index - result);                   index = i;             }         }         ans as  i32      } } 
  最终只对 nums1 和 nums2 做了两次遍历,时间复杂度 O ( n ) O(n) O ( n ) O ( 1 ) O(1) O ( 1 ) 
2022 年 10 月 09 日 
解题思路 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 impl  Solution {    pub  fn  score_of_parentheses String ) -> i32  {         let  mut  stack = vec! [0 ];         for  ch in  s.chars() {             match  ch {                 '('  => stack.push(0 ),                 ')'  => {                     let  top = stack.pop().unwrap();                     let  newtop = if  top == 0  {                         stack.pop().unwrap() + 1                      } else  {                         stack.pop().unwrap() + top * 2                      };                     stack.push(newtop);                 }                 _ => panic! ()             }         }         stack[0 ]     } } 
  解释:top 为 0 时,意味着遇到了 (),计算结果为 1;不为 0 时,代表括号内包含其它内容,那么将其计算结果乘二后加到父级括号上。最终算法时间复杂度 O ( n ) O(n) O ( n ) O ( n ) O(n) O ( n )