diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index b84d1d3c..4f39f60a 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -455,42 +455,31 @@ function letterCombinations(digits: string): string[] { ## Rust ```Rust +const map: [&str; 10] = [ + "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz", +]; impl Solution { - fn backtracking(result: &mut Vec, s: &mut String, map: &[&str; 10], digits: &String, index: usize) { + fn back_trace(result: &mut Vec, s: &mut String, digits: &String, index: usize) { let len = digits.len(); if len == index { result.push(s.to_string()); return; } - // 在保证不会越界的情况下使用unwrap()将Some()中的值提取出来 - let digit= digits.chars().nth(index).unwrap().to_digit(10).unwrap() as usize; - let letters = map[digit]; - for i in letters.chars() { + let digit = (digits.as_bytes()[index] - b'0') as usize; + for i in map[digit].chars() { s.push(i); - Self::backtracking(result, s, &map, &digits, index+1); + Self::back_trace(result, s, digits, index + 1); s.pop(); } } pub fn letter_combinations(digits: String) -> Vec { - if digits.len() == 0 { + if digits.is_empty() { return vec![]; } - const MAP: [&str; 10] = [ - "", - "", - "abc", - "def", - "ghi", - "jkl", - "mno", - "pqrs", - "tuv", - "wxyz" - ]; - let mut result: Vec = Vec::new(); - let mut s: String = String::new(); - Self::backtracking(&mut result, &mut s, &MAP, &digits, 0); - result + let mut res = vec![]; + let mut s = String::new(); + Self::back_trace(&mut res, &mut s, &digits, 0); + res } } ``` diff --git a/problems/0024.两两交换链表中的节点.md b/problems/0024.两两交换链表中的节点.md index 50775cdd..4dc051aa 100644 --- a/problems/0024.两两交换链表中的节点.md +++ b/problems/0024.两两交换链表中的节点.md @@ -154,24 +154,25 @@ class Solution { ``` ```java -// 虚拟头结点 class Solution { public ListNode swapPairs(ListNode head) { - - ListNode dummyNode = new ListNode(0); - dummyNode.next = head; - ListNode prev = dummyNode; - - while (prev.next != null && prev.next.next != null) { - ListNode temp = head.next.next; // 缓存 next - prev.next = head.next; // 将 prev 的 next 改为 head 的 next - head.next.next = head; // 将 head.next(prev.next) 的next,指向 head - head.next = temp; // 将head 的 next 接上缓存的temp - prev = head; // 步进1位 - head = head.next; // 步进1位 + ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点 + dumyhead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作 + ListNode cur = dumyhead; + ListNode temp; // 临时节点,保存两个节点后面的节点 + ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点 + ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点 + while (cur.next != null && cur.next.next != null) { + temp = cur.next.next.next; + firstnode = cur.next; + secondnode = cur.next.next; + cur.next = secondnode; // 步骤一 + secondnode.next = firstnode; // 步骤二 + firstnode.next = temp; // 步骤三 + cur = firstnode; // cur移动,准备下一轮交换 + } + return dumyhead.next; } - return dummyNode.next; - } } ``` diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md index 34318d16..066e1b33 100644 --- a/problems/0077.组合优化.md +++ b/problems/0077.组合优化.md @@ -267,22 +267,22 @@ Rust: ```Rust impl Solution { - fn backtracking(result: &mut Vec>, path: &mut Vec, n: i32, k: i32, startIndex: i32) { + fn backtracking(result: &mut Vec>, path: &mut Vec, n: i32, k: i32, start_index: i32) { let len= path.len() as i32; if len == k{ result.push(path.to_vec()); return; } // 此处剪枝 - for i in startIndex..= n - (k - len) + 1 { + for i in start_index..= n - (k - len) + 1 { path.push(i); Self::backtracking(result, path, n, k, i+1); path.pop(); } } pub fn combine(n: i32, k: i32) -> Vec> { - let mut result: Vec> = Vec::new(); - let mut path: Vec = Vec::new(); + let mut result = vec![]; + let mut path = vec![]; Self::backtracking(&mut result, &mut path, n, k, 1); result } diff --git a/problems/0098.验证二叉搜索树.md b/problems/0098.验证二叉搜索树.md index 95ae783f..93c2272c 100644 --- a/problems/0098.验证二叉搜索树.md +++ b/problems/0098.验证二叉搜索树.md @@ -392,7 +392,26 @@ class Solution: return is_left_valid and is_right_valid return __isValidBST(root) ``` - +**递归** - 避免初始化最小值做法: +```python +class Solution: + def isValidBST(self, root: TreeNode) -> bool: + # 规律: BST的中序遍历节点数值是从小到大. + pre = None + def __isValidBST(root: TreeNode) -> bool: + nonlocal pre + + if not root: + return True + + is_left_valid = __isValidBST(root.left) + if pre and pre.val>=root.val: return False + pre = root + is_right_valid = __isValidBST(root.right) + + return is_left_valid and is_right_valid + return __isValidBST(root) +``` ```python 迭代-中序遍历 class Solution: diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index a565d052..8ae7ee79 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -271,6 +271,36 @@ func levelOrder(root *TreeNode) [][]int { return res } + +/** +102. 二叉树的层序遍历:使用切片模拟队列,易理解 + */ +func levelOrder(root *TreeNode) (res [][]int) { + if root == nil { + return + } + + curLevel := []*TreeNode{root} // 存放当前层节点 + for len(curLevel) > 0 { + nextLevel := []*TreeNode{} // 准备通过当前层生成下一层 + vals := []int{} + + for _, node := range curLevel { + vals = append(vals, node.Val) // 收集当前层的值 + // 收集下一层的节点 + if node.Left != nil { + nextLevel = append(nextLevel, node.Left) + } + if node.Right != nil { + nextLevel = append(nextLevel, node.Right) + } + } + res = append(res, vals) + curLevel = nextLevel // 将下一层变成当前层 + } + + return +} ``` javascript代码: @@ -1072,7 +1102,6 @@ public class N0637 { que.offerLast(root); while (!que.isEmpty()) { - TreeNode peek = que.peekFirst(); int levelSize = que.size(); double levelSum = 0.0; @@ -1346,6 +1375,22 @@ class Solution: return results ``` +```python +# LeetCode 429. N-ary Tree Level Order Traversal +# 递归法 +class Solution: + def levelOrder(self, root: 'Node') -> List[List[int]]: + if not root: return [] + result=[] + def traversal(root,depth): + if len(result)==depth:result.append([]) + result[depth].append(root.val) + if root.children: + for i in range(len(root.children)):traversal(root.children[i],depth+1) + + traversal(root,0) + return result +``` java: ```java @@ -2955,7 +3000,7 @@ impl Solution { * 107.二叉树的层次遍历II * 199.二叉树的右视图 * 637.二叉树的层平均值 -* 429.N叉树的前序遍历 +* 429.N叉树的层序遍历 * 515.在每个树行中找最大值 * 116.填充每个节点的下一个右侧节点指针 * 117.填充每个节点的下一个右侧节点指针II @@ -2970,3 +3015,4 @@ impl Solution { + diff --git a/problems/0104.二叉树的最大深度.md b/problems/0104.二叉树的最大深度.md index 03322c9a..e54221db 100644 --- a/problems/0104.二叉树的最大深度.md +++ b/problems/0104.二叉树的最大深度.md @@ -402,10 +402,10 @@ class solution: def getdepth(self, node): if not node: return 0 - leftdepth = self.getdepth(node.left) #左 - rightdepth = self.getdepth(node.right) #右 - depth = 1 + max(leftdepth, rightdepth) #中 - return depth + leftheight = self.getdepth(node.left) #左 + rightheight = self.getdepth(node.right) #右 + height = 1 + max(leftheight, rightheight) #中 + return height ``` 递归法:精简代码 diff --git a/problems/0108.将有序数组转换为二叉搜索树.md b/problems/0108.将有序数组转换为二叉搜索树.md index 01c276fd..c75f87d1 100644 --- a/problems/0108.将有序数组转换为二叉搜索树.md +++ b/problems/0108.将有序数组转换为二叉搜索树.md @@ -486,6 +486,26 @@ object Solution { } ``` +## rust + +递归: + +```rust +impl Solution { + pub fn sorted_array_to_bst(nums: Vec) -> Option>> { + if nums.is_empty() { + return None; + } + let index = nums.len() / 2; + let mut root = TreeNode::new(nums[index]); + + root.left = Self::sorted_array_to_bst(nums[..index].to_vec()); + root.right = Self::sorted_array_to_bst(nums[index + 1..].to_vec()); + Some(Rc::new(RefCell::new(root))) + } +} +``` +

diff --git a/problems/0121.买卖股票的最佳时机.md b/problems/0121.买卖股票的最佳时机.md index 915b178a..8736c9f3 100644 --- a/problems/0121.买卖股票的最佳时机.md +++ b/problems/0121.买卖股票的最佳时机.md @@ -89,7 +89,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金 **注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态** -很多同学把“持有”和“买入”没分区分清楚。 +很多同学把“持有”和“买入”没区分清楚。 在下面递推公式分析中,我会进一步讲解。 @@ -103,11 +103,11 @@ dp[i][1] 表示第i天不持有股票所得最多现金 如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来 * 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1] -* 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0] +* 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0] 同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); -这样递归公式我们就分析完了 +这样递推公式我们就分析完了 3. dp数组如何初始化 @@ -121,7 +121,7 @@ dp[0][1]表示第0天不持有股票,不持有股票那么现金就是0,所 4. 确定遍历顺序 -从递推公式可以看出dp[i]都是有dp[i - 1]推导出来的,那么一定是从前向后遍历。 +从递推公式可以看出dp[i]都是由dp[i - 1]推导出来的,那么一定是从前向后遍历。 5. 举例推导dp数组 @@ -326,53 +326,40 @@ Go: > 贪心法: ```Go func maxProfit(prices []int) int { - low := math.MaxInt32 - rlt := 0 - for i := range prices{ - low = min(low, prices[i]) - rlt = max(rlt, prices[i]-low) + min := prices[0] + res := 0 + for i := 1; i < len(prices); i++ { + if prices[i] - min > res { + res = prices[i]-min + } + if min > prices[i] { + min = prices[i] + } } - - return rlt -} -func min(a, b int) int { - if a < b{ - return a - } - - return b -} - -func max(a, b int) int { - if a > b{ - return a - } - - return b + return res } ``` > 动态规划:版本一 ```Go func maxProfit(prices []int) int { - length:=len(prices) - if length==0{return 0} - dp:=make([][]int,length) - for i:=0;ib{ +func max(a, b int) int { + if a > b { return a } return b @@ -385,7 +372,7 @@ func maxProfit(prices []int) int { dp := [2][2]int{} dp[0][0] = -prices[0] dp[0][1] = 0 - for i := 1; i < len(prices); i++{ + for i := 1; i < len(prices); i++ { dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i]) dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i]) } diff --git a/problems/0122.买卖股票的最佳时机II(动态规划).md b/problems/0122.买卖股票的最佳时机II(动态规划).md index f2aec68b..2779083d 100644 --- a/problems/0122.买卖股票的最佳时机II(动态规划).md +++ b/problems/0122.买卖股票的最佳时机II(动态规划).md @@ -39,7 +39,7 @@ 本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法:买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。 -本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票) +本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别是本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票) **在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的**。 @@ -63,9 +63,9 @@ 那么第i天持有股票即dp[i][0],如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即:dp[i - 1][1] - prices[i]。 -在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来 +再来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来 * 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1] -* 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0] +* 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0] **注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!** @@ -99,7 +99,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); **这正是因为本题的股票可以买卖多次!** 所以买入股票的时候,可能会有之前买卖的利润即:dp[i - 1][1],所以dp[i - 1][1] - prices[i]。 -想到到这一点,对这两道题理解的比较深刻了。 +想到到这一点,对这两道题理解的就比较深刻了。 这里我依然给出滚动数组的版本,C++代码如下: @@ -228,29 +228,6 @@ func max(a, b int) int { } ``` -```go -func maxProfit(prices []int) int { - //创建数组 - dp:=make([][]int,len(prices)) - for i:=0;ib{ +func max(a, b int) int { + if a > b { return a } return b @@ -407,39 +407,6 @@ function maxProfit(prices: number[]): number { }; ``` -Go: - -> 版本一: -```go -// 买卖股票的最佳时机III 动态规划 -// 时间复杂度O(n) 空间复杂度O(n) -func maxProfit(prices []int) int { - dp := make([][]int, len(prices)) - status := make([]int, len(prices) * 4) - for i := range dp { - dp[i] = status[:4] - status = status[4:] - } - dp[0][0], dp[0][2] = -prices[0], -prices[0] - - for i := 1; i < len(prices); i++ { - dp[i][0] = max(dp[i - 1][0], -prices[i]) - dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]) - dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] - prices[i]) - dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] + prices[i]) - } - - return dp[len(prices) - 1][3] -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} -``` - diff --git a/problems/0127.单词接龙.md b/problems/0127.单词接龙.md index 2e30123d..bc21d0ad 100644 --- a/problems/0127.单词接龙.md +++ b/problems/0127.单词接龙.md @@ -247,6 +247,55 @@ var ladderLength = function(beginWord, endWord, wordList) { }; ``` +## TypeScript +```typescript +function ladderLength( + beginWord: string, + endWord: string, + wordList: string[] +): number { + const words = new Set(wordList); + if (!words.has(endWord)) return 0; + if (beginWord.length === 1) return 2; + let current = new Set([beginWord]); + let rightcurrent = new Set([endWord]); + words.delete(endWord); + let step = 1; + while (current.size) { + if (current.size > rightcurrent.size) { + [current, rightcurrent] = [rightcurrent, current]; + } + const temp: Set = new Set(); + for (const word of current) { + for (const right of rightcurrent) { + if (diffonechar(word, right)) { + return step + 1; + } + } + for (const other of words) { + if (diffonechar(other, word)) { + temp.add(other); + + words.delete(other); + } + } + } + if (temp.size === 0) return 0; + current = temp; + step = step + 1; + } + return 0; +} + +function diffonechar(word1: string, word2: string): boolean { + let changes = 0; + for (let i = 0; i < word1.length; i++) { + if (word1[i] != word2[i]) changes += 1; + } + return changes === 1; +} +``` +

diff --git a/problems/0131.分割回文串.md b/problems/0131.分割回文串.md index 79ab72ee..30bba455 100644 --- a/problems/0131.分割回文串.md +++ b/problems/0131.分割回文串.md @@ -490,15 +490,15 @@ var partition = function(s) { const res = [], path = [], len = s.length; backtracking(0); return res; - function backtracking(i) { - if(i >= len) { + function backtracking(startIndex) { + if(startIndex >= len) { res.push(Array.from(path)); return; } - for(let j = i; j < len; j++) { - if(!isPalindrome(s, i, j)) continue; - path.push(s.slice(i, j + 1)); - backtracking(j + 1); + for(let i = startIndex; i < len; i++) { + if(!isPalindrome(s, startIndex, i)) continue; + path.push(s.slice(startIndex, i + 1)); + backtracking(i + 1); path.pop(); } } diff --git a/problems/0139.单词拆分.md b/problems/0139.单词拆分.md index f06bd40c..f76d8075 100644 --- a/problems/0139.单词拆分.md +++ b/problems/0139.单词拆分.md @@ -138,7 +138,7 @@ public: 3. dp数组如何初始化 -从递归公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递归的根基,dp[0]一定要为true,否则递归下去后面都都是false了。 +从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false了。 那么dp[0]有没有意义呢? @@ -152,13 +152,13 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。 题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。 -还要讨论两层for循环的前后循序。 +还要讨论两层for循环的前后顺序。 **如果求组合数就是外层for循环遍历物品,内层for遍历背包**。 **如果求排列数就是外层for遍历背包,内层for循环遍历物品**。 -我在这里做一个一个总结: +我在这里做一个总结: 求组合数:[动态规划:518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 求排列数:[动态规划:377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)、[动态规划:70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html) @@ -170,7 +170,7 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。 "apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的,那么我们就是强调物品之间顺序。 -所以说,本题一定是 先遍历 背包,在遍历物品。 +所以说,本题一定是 先遍历 背包,再遍历物品。 5. 举例推导dp[i] @@ -209,7 +209,7 @@ public: 关于遍历顺序,再给大家讲一下为什么 先遍历物品再遍历背包不行。 -这里可以给出先遍历物品在遍历背包的代码: +这里可以给出先遍历物品再遍历背包的代码: ```CPP class Solution { @@ -241,7 +241,7 @@ public: 最后dp[s.size()] = 0 即 dp[13] = 0 ,而不是1,因为先用 "apple" 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1。 -除非是先用 "apple" 遍历一遍,在用 "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1。 +除非是先用 "apple" 遍历一遍,再用 "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1。 如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。 @@ -352,16 +352,16 @@ class Solution: Go: ```Go func wordBreak(s string,wordDict []string) bool { - wordDictSet:=make(map[string]bool) - for _,w:=range wordDict{ - wordDictSet[w]=true + wordDictSet := make(map[string]bool) + for _, w := range wordDict { + wordDictSet[w] = true } - dp:=make([]bool,len(s)+1) - dp[0]=true - for i:=1;i<=len(s);i++{ - for j:=0;jb{ - return a - } - return b -} -``` - Javascript: ```javascript diff --git a/problems/0198.打家劫舍.md b/problems/0198.打家劫舍.md index 1e48f007..6002cd3a 100644 --- a/problems/0198.打家劫舍.md +++ b/problems/0198.打家劫舍.md @@ -52,7 +52,7 @@ 如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。 -如果不偷第i房间,那么dp[i] = dp[i - 1],即考虑i-1房,(**注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点**) +如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(**注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点**) 然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); @@ -154,29 +154,20 @@ class Solution: Go: ```Go func rob(nums []int) int { - if len(nums)<1{ - return 0 - } - if len(nums)==1{ - return nums[0] - } - if len(nums)==2{ - return max(nums[0],nums[1]) - } - dp :=make([]int,len(nums)) - dp[0]=nums[0] - dp[1]=max(nums[0],nums[1]) - for i:=2;ib{ - return a - } - return b + if a > b { + return a + } + return b } ``` diff --git a/problems/0213.打家劫舍II.md b/problems/0213.打家劫舍II.md index 8e1ca126..e595d2fd 100644 --- a/problems/0213.打家劫舍II.md +++ b/problems/0213.打家劫舍II.md @@ -131,7 +131,7 @@ class Solution: val2=self.roblist(nums[:-1])#不偷最后一间房 return max(val1,val2) - def robRange(self,nums): + def roblist(self,nums): l=len(nums) dp=[0]*l dp[0]=nums[0] @@ -143,6 +143,44 @@ class Solution: return dp[-1] ``` +Go: + +```go +// 打家劫舍Ⅱ 动态规划 +// 时间复杂度O(n) 空间复杂度O(n) +func rob(nums []int) int { + if len(nums) == 1 { + return nums[0] + } + if len(nums) == 2 { + return max(nums[0], nums[1]) + } + + result1 := robRange(nums, 0) + result2 := robRange(nums, 1) + return max(result1, result2) +} + +// 偷盗指定的范围 +func robRange(nums []int, start int) int { + dp := make([]int, len(nums)) + dp[1] = nums[start] + + for i := 2; i < len(nums); i++ { + dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1]) + } + + return dp[len(nums) - 1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + javascipt: ```javascript var rob = function(nums) { @@ -187,44 +225,6 @@ function robRange(nums: number[], start: number, end: number): number { } ``` -Go: - -```go -// 打家劫舍Ⅱ 动态规划 -// 时间复杂度O(n) 空间复杂度O(n) -func rob(nums []int) int { - if len(nums) == 1 { - return nums[0] - } - if len(nums) == 2 { - return max(nums[0], nums[1]) - } - - result1 := robRange(nums, 0) - result2 := robRange(nums, 1) - return max(result1, result2) -} - -// 偷盗指定的范围 -func robRange(nums []int, start int) int { - dp := make([]int, len(nums)) - dp[1] = nums[start] - - for i := 2; i < len(nums); i++ { - dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1]) - } - - return dp[len(nums) - 1] -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} -``` -

diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md index a218fb46..5ca7bb90 100644 --- a/problems/0216.组合总和III.md +++ b/problems/0216.组合总和III.md @@ -479,28 +479,36 @@ function combinationSum3(k: number, n: number): number[][] { ```Rust impl Solution { - fn backtracking(result: &mut Vec>, path:&mut Vec, targetSum:i32, k: i32, mut sum: i32, startIndex: i32) { + pub fn combination_sum3(k: i32, n: i32) -> Vec> { + let mut result = vec![]; + let mut path = vec![]; + Self::backtrace(&mut result, &mut path, n, k, 0, 1); + result + } + pub fn backtrace( + result: &mut Vec>, + path: &mut Vec, + target_sum: i32, + k: i32, + sum: i32, + start_index: i32, + ) { + if sum > target_sum { + return; + } let len = path.len() as i32; if len == k { - if sum == targetSum { + if sum == target_sum { result.push(path.to_vec()); } return; } - for i in startIndex..=9 { - sum += i; + for i in start_index..=9 - (k - len) + 1 { path.push(i); - Self::backtracking(result, path, targetSum, k, sum, i+1); - sum -= i; + Self::backtrace(result, path, target_sum, k, sum + i, i + 1); path.pop(); } } - pub fn combination_sum3(k: i32, n: i32) -> Vec> { - let mut result: Vec> = Vec::new(); - let mut path: Vec = Vec::new(); - Self::backtracking(&mut result, &mut path, n, k, 0, 1); - result - } } ``` diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index 62e154b8..1ea9a3e6 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -322,6 +322,18 @@ class Solution: return root ``` +递归法:后序遍历: +```python +class Solution: + def invertTree(self, root: TreeNode) -> TreeNode: + if root is None: + return None + self.invertTree(root.left) + self.invertTree(root.right) + root.left, root.right = root.right, root.left + return root +``` + 迭代法:深度优先遍历(前序遍历): ```python class Solution: @@ -359,7 +371,22 @@ class Solution: queue.append(node.right) return root ``` - +迭代法:广度优先遍历(层序遍历),和之前的层序遍历写法一致: +```python +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: return root + from collections import deque + que=deque([root]) + while que: + size=len(que) + for i in range(size): + cur=que.popleft() + cur.left, cur.right = cur.right, cur.left + if cur.left: que.append(cur.left) + if cur.right: que.append(cur.right) + return root +``` ### Go 递归版本的前序遍历 diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index c048c929..1c21f2be 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -429,6 +429,67 @@ object Solution { } ``` +## rust + +递归: + +```rust +impl Solution { + pub fn lowest_common_ancestor( + root: Option>>, + p: Option>>, + q: Option>>, + ) -> Option>> { + let q_val = q.as_ref().unwrap().borrow().val; + let p_val = p.as_ref().unwrap().borrow().val; + let root_val = root.as_ref().unwrap().borrow().val; + + if root_val > q_val && root_val > p_val { + return Self::lowest_common_ancestor( + root.as_ref().unwrap().borrow().left.clone(), + p, + q, + ); + }; + + if root_val < q_val && root_val < p_val { + return Self::lowest_common_ancestor( + root.as_ref().unwrap().borrow().right.clone(), + p, + q, + ); + } + root + } +} +``` + +迭代: + +```rust +impl Solution { + pub fn lowest_common_ancestor( + mut root: Option>>, + p: Option>>, + q: Option>>, + ) -> Option>> { + let p_val = p.unwrap().borrow().val; + let q_val = q.unwrap().borrow().val; + while let Some(node) = root.clone() { + let root_val = node.borrow().val; + if root_val > q_val && root_val > p_val { + root = node.borrow().left.clone(); + } else if root_val < q_val && root_val < p_val { + root = node.borrow().right.clone(); + } else { + return root; + } + } + None + } +} +``` +

diff --git a/problems/0239.滑动窗口最大值.md b/problems/0239.滑动窗口最大值.md index 9f2e96a6..540ab5d7 100644 --- a/problems/0239.滑动窗口最大值.md +++ b/problems/0239.滑动窗口最大值.md @@ -74,7 +74,7 @@ public: 大家此时应该陷入深思..... -**其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里的元素数值是由大到小的。** +**其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。** 那么这个维护元素单调递减的队列就叫做**单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列** diff --git a/problems/0300.最长上升子序列.md b/problems/0300.最长上升子序列.md index 42efe31d..f4fe1c31 100644 --- a/problems/0300.最长上升子序列.md +++ b/problems/0300.最长上升子序列.md @@ -36,9 +36,9 @@ 首先通过本题大家要明确什么是子序列,“子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序”。 本题也是代码随想录中子序列问题的第一题,如果没接触过这种题目的话,本题还是很难的,甚至想暴力去搜索也不知道怎么搜。 -子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下表j的子序列长度有关系,那那又是什么样的关系呢。 +子序列问题是动态规划解决的经典问题,当前下标i的递增子序列长度,其实和i之前的下表j的子序列长度有关系,那又是什么样的关系呢。 -接下来,我们依然用动规五部曲来分析详细一波: +接下来,我们依然用动规五部曲来详细分析一波: 1. dp[i]的定义 @@ -46,7 +46,7 @@ **dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度** -为什么一定表示 “以nums[i]结尾的最长递增子序” ,因为我们在 做 递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小,那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾, 要不然这个比较就没有意义了,不是尾部元素的比较那么 如果算递增呢。 +为什么一定表示 “以nums[i]结尾的最长递增子序” ,因为我们在 做 递增比较的时候,如果比较 nums[j] 和 nums[i] 的大小,那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾, 要不然这个比较就没有意义了,不是尾部元素的比较那么 如何算递增呢。 2. 状态转移方程 @@ -155,31 +155,6 @@ class Solution: ``` Go: -```go -func lengthOfLIS(nums []int ) int { - dp := []int{} - for _, num := range nums { - if len(dp) ==0 || dp[len(dp) - 1] < num { - dp = append(dp, num) - } else { - l, r := 0, len(dp) - 1 - pos := r - for l <= r { - mid := (l + r) >> 1 - if dp[mid] >= num { - pos = mid; - r = mid - 1 - } else { - l = mid + 1 - } - } - dp[pos] = num - }//二分查找 - } - return len(dp) -} -``` - ```go // 动态规划求解 func lengthOfLIS(nums []int) int { @@ -212,21 +187,29 @@ func max(x, y int) int { return y } ``` - -Rust: -```rust -pub fn length_of_lis(nums: Vec) -> i32 { - let mut dp = vec![1; nums.len() + 1]; - let mut result = 1; - for i in 1..nums.len() { - for j in 0..i { - if nums[j] < nums[i] { - dp[i] = dp[i].max(dp[j] + 1); - } - result = result.max(dp[i]); - } - } - result +贪心+二分 优化 +```go +func lengthOfLIS(nums []int ) int { + dp := []int{} + for _, num := range nums { + if len(dp) == 0 || dp[len(dp) - 1] < num { + dp = append(dp, num) + } else { + l, r := 0, len(dp) - 1 + pos := r + for l <= r { + mid := (l + r) >> 1 + if dp[mid] >= num { + pos = mid; + r = mid - 1 + } else { + l = mid + 1 + } + } + dp[pos] = num + }//二分查找 + } + return len(dp) } ``` @@ -270,6 +253,22 @@ function lengthOfLIS(nums: number[]): number { }; ``` +Rust: +```rust +pub fn length_of_lis(nums: Vec) -> i32 { + let mut dp = vec![1; nums.len() + 1]; + let mut result = 1; + for i in 1..nums.len() { + for j in 0..i { + if nums[j] < nums[i] { + dp[i] = dp[i].max(dp[j] + 1); + } + result = result.max(dp[i]); + } + } + result +} +``` diff --git a/problems/0337.打家劫舍III.md b/problems/0337.打家劫舍III.md index c69279fd..ba9a2e59 100644 --- a/problems/0337.打家劫舍III.md +++ b/problems/0337.打家劫舍III.md @@ -129,7 +129,7 @@ if (cur == NULL) return vector{0, 0}; 3. 确定遍历顺序 -首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。 +首先明确的是使用后序遍历。 因为要通过递归函数的返回值来做下一步计算。 通过递归左节点,得到左节点偷与不偷的金钱。 @@ -147,7 +147,7 @@ vector right = robTree(cur->right); // 右 4. 确定单层递归的逻辑 -如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (**如果对下标含义不理解就在回顾一下dp数组的含义**) +如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (**如果对下标含义不理解就再回顾一下dp数组的含义**) 如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的,所以:val2 = max(left[0], left[1]) + max(right[0], right[1]); @@ -483,37 +483,6 @@ function robNode(node: TreeNode | null): MaxValueArr { } ``` -### Go - -```go -// 打家劫舍Ⅲ 动态规划 -// 时间复杂度O(n) 空间复杂度O(logn) -func rob(root *TreeNode) int { - dp := traversal(root) - return max(dp[0], dp[1]) -} - -func traversal(cur *TreeNode) []int { - if cur == nil { - return []int{0, 0} - } - - dpL := traversal(cur.Left) - dpR := traversal(cur.Right) - - val1 := cur.Val + dpL[0] + dpR[0] // 偷盗当前节点 - val2 := max(dpL[0], dpL[1]) + max(dpR[0], dpR[1]) // 不偷盗当前节点 - return []int{val2, val1} -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} -``` -

diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md index c0196973..82b3604e 100644 --- a/problems/0491.递增子序列.md +++ b/problems/0491.递增子序列.md @@ -9,7 +9,7 @@ # 491.递增子序列 -[力扣题目链接](https://leetcode.cn/problems/increasing-subsequences/) +[力扣题目链接](https://leetcode.cn/problems/non-decreasing-subsequences/) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。 @@ -399,7 +399,7 @@ var findSubsequences = function(nums) { ``` -## TypeScript +### TypeScript ```typescript function findSubsequences(nums: number[]): number[][] { @@ -545,7 +545,7 @@ int** findSubsequences(int* nums, int numsSize, int* returnSize, int** returnCol } ``` -## Swift +### Swift ```swift func findSubsequences(_ nums: [Int]) -> [[Int]] { @@ -576,7 +576,7 @@ func findSubsequences(_ nums: [Int]) -> [[Int]] { ``` -## Scala +### Scala ```scala object Solution { @@ -614,3 +614,4 @@ object Solution { + diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md index ec12c525..3cb9d3db 100644 --- a/problems/0538.把二叉搜索树转换为累加树.md +++ b/problems/0538.把二叉搜索树转换为累加树.md @@ -375,6 +375,55 @@ object Solution { } ``` +## rust + +递归: + +```rust +impl Solution { + pub fn convert_bst(root: Option>>) -> Option>> { + let mut pre = 0; + Self::traversal(&root, &mut pre); + root + } + + pub fn traversal(cur: &Option>>, pre: &mut i32) { + if cur.is_none() { + return; + } + let mut node = cur.as_ref().unwrap().borrow_mut(); + Self::traversal(&node.right, pre); + *pre += node.val; + node.val = *pre; + Self::traversal(&node.left, pre); + } +} +``` + +迭代: + +```rust +impl Solution { + pub fn convert_bst(root: Option>>) -> Option>> { + let mut cur = root.clone(); + let mut stack = vec![]; + let mut pre = 0; + while !stack.is_empty() || cur.is_some() { + while let Some(node) = cur { + cur = node.borrow().right.clone(); + stack.push(node); + } + if let Some(node) = stack.pop() { + pre += node.borrow().val; + node.borrow_mut().val = pre; + cur = node.borrow().left.clone(); + } + } + root + } +} +``` +

diff --git a/problems/0669.修剪二叉搜索树.md b/problems/0669.修剪二叉搜索树.md index 1c5fdea1..ac36509f 100644 --- a/problems/0669.修剪二叉搜索树.md +++ b/problems/0669.修剪二叉搜索树.md @@ -473,6 +473,33 @@ object Solution { } ``` +## rust + +// 递归 +```rust +impl Solution { + pub fn trim_bst( + root: Option>>, + low: i32, + high: i32, + ) -> Option>> { + root.as_ref()?; + let mut node = root.as_ref().unwrap().borrow_mut(); + if node.val < low { + return Self::trim_bst(node.right.clone(), low, high); + } + if node.val > high { + return Self::trim_bst(node.left.clone(), low, high); + } + + node.left = Self::trim_bst(node.left.clone(), low, high); + node.right = Self::trim_bst(node.right.clone(), low, high); + drop(node); + root + } +} +``` +

diff --git a/problems/0701.二叉搜索树中的插入操作.md b/problems/0701.二叉搜索树中的插入操作.md index 955e9d33..4bdb8d60 100644 --- a/problems/0701.二叉搜索树中的插入操作.md +++ b/problems/0701.二叉搜索树中的插入操作.md @@ -645,6 +645,66 @@ object Solution { } ``` +### rust + +迭代: + +```rust +impl Solution { + pub fn insert_into_bst( + root: Option>>, + val: i32, + ) -> Option>> { + if root.is_none() { + return Some(Rc::new(RefCell::new(TreeNode::new(val)))); + } + let mut cur = root.clone(); + let mut pre = None; + while let Some(node) = cur.clone() { + pre = cur; + if node.borrow().val > val { + cur = node.borrow().left.clone(); + } else { + cur = node.borrow().right.clone(); + }; + } + let r = Some(Rc::new(RefCell::new(TreeNode::new(val)))); + let mut p = pre.as_ref().unwrap().borrow_mut(); + if val < p.val { + p.left = r; + } else { + p.right = r; + } + + root + } +} +``` + +递归: + +```rust +impl Solution { + pub fn insert_into_bst( + root: Option>>, + val: i32, + ) -> Option>> { + if let Some(node) = &root { + if node.borrow().val > val { + let left = Self::insert_into_bst(node.borrow_mut().left.take(), val); + node.borrow_mut().left = left; + } else { + let right = Self::insert_into_bst(node.borrow_mut().right.take(), val); + node.borrow_mut().right = right; + } + root + } else { + Some(Rc::new(RefCell::new(TreeNode::new(val)))) + } + } +} +``` +

diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md index 614bb36e..b82a645e 100644 --- a/problems/0707.设计链表.md +++ b/problems/0707.设计链表.md @@ -157,7 +157,7 @@ private: ## 其他语言版本 C: ```C -typedef struct { +typedef struct MyLinkedList { int val; struct MyLinkedList* next; }MyLinkedList; @@ -233,7 +233,7 @@ void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) { MyLinkedList *tmp = obj->next; if (tmp != NULL){ obj->next = tmp->next; - free(tmp) + free(tmp); } return; } diff --git a/problems/0718.最长重复子数组.md b/problems/0718.最长重复子数组.md index 37f6cbfe..0658f9c8 100644 --- a/problems/0718.最长重复子数组.md +++ b/problems/0718.最长重复子数组.md @@ -28,7 +28,7 @@ 注意题目中说的子数组,其实就是连续子序列。 -要求两个数组中最长重复子数组,如果是暴力的解法 只要需要先两层for循环确定两个数组起始位置,然后在来一个循环可以是for或者while,来从两个起始位置开始比较,取得重复子数组的长度。 +要求两个数组中最长重复子数组,如果是暴力的解法 只需要先两层for循环确定两个数组起始位置,然后再来一个循环可以是for或者while,来从两个起始位置开始比较,取得重复子数组的长度。 本题其实是动规解决的经典题目,我们只要想到 用二维数组可以记录两个字符串的所有比较情况,这样就比较好推 递推公式了。 动规五部曲分析如下: @@ -163,7 +163,7 @@ public: 当然可以,就是实现起来麻烦一些。 -如果定义 dp[i][j]为 以下标i为结尾的A,和以下标j 为结尾的B,那么 第一行和第一列毕竟要经行初始化,如果nums1[i] 与 nums2[0] 相同的话,对应的 dp[i][0]就要初始为1, 因为此时最长重复子数组为1。 nums2[j] 与 nums1[0]相同的话,同理。 +如果定义 dp[i][j]为 以下标i为结尾的A,和以下标j 为结尾的B,那么 第一行和第一列毕竟要进行初始化,如果nums1[i] 与 nums2[0] 相同的话,对应的 dp[i][0]就要初始为1, 因为此时最长重复子数组为1。 nums2[j] 与 nums1[0]相同的话,同理。 所以代码如下: @@ -298,6 +298,29 @@ func findLength(A []int, B []int) int { } return res } + +// 滚动数组 +func findLength(nums1 []int, nums2 []int) int { + n, m, res := len(nums1), len(nums2), 0 + dp := make([]int, m+1) + for i := 1; i <= n; i++ { + for j := m; j >= 1; j-- { + if nums1[i-1] == nums2[j-1] { + dp[j] = dp[j-1] + 1 + } else { + dp[j] = 0 // 注意这里不相等要赋值为0,供下一层使用 + } + res = max(res, dp[j]) + } + } + return res +} +func max(a, b int) int { + if a > b { + return a + } + return b +} ``` JavaScript: diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md index e4a0512a..d897755b 100644 --- a/problems/0968.监控二叉树.md +++ b/problems/0968.监控二叉树.md @@ -577,6 +577,91 @@ object Solution { result } } +``` +### Rust +```Rust +/// 版本一 +impl Solution { + pub fn min_camera_cover(root: Option>>) -> i32 { + let mut res = 0; + if Self::traversal(&root, &mut res) == 0 { + res += 1; + } + res + } + + pub fn traversal(cur: &Option>>, ans: &mut i32) -> i32 { + // 0 未覆盖 1 节点已设置摄像头 2 节点已覆盖 + if let Some(node) = cur { + let node = node.borrow(); + + let left = Self::traversal(&node.left, ans); + let right = Self::traversal(&node.right, ans); + + // 左右节点都被覆盖 + if left == 2 && right == 2 { + return 0; // 无覆盖 + } + + // left == 0 right == 0 左右无覆盖 + // left == 0 right == 1 左节点无覆盖 右节点有摄像头 + // left == 1 right == 0 左节点有摄像头 左节点无覆盖 + // left == 0 right == 2 左节点无覆盖 右节点有覆盖 + // left == 2 right == 0 左节点有覆盖 右节点无覆盖 + if left == 0 || right == 0 { + *ans += 1; + return 1; + } + + // left == 1 right == 1 左节点有摄像头 右节点有摄像头 + // left == 1 right == 2 左节点有摄像头 右节点覆盖 + // left == 2 right == 1 左节点覆盖 右节点有摄像头 + if left == 1 || right == 1 { + return 2; // 已覆盖 + } + } else { + return 2; + } + -1 + } +} + +/// 版本二 +enum NodeState { + NoCover = 0, + Camera = 1, + Covered = 2, +} + +impl Solution { + pub fn min_camera_cover(root: Option>>) -> i32 { + let mut res = 0; + let state = Self::traversal(&root, &mut res); + match state { + NodeState::NoCover => res + 1, + _ => res, + } + } + + pub fn traversal(cur: &Option>>, ans: &mut i32) -> NodeState { + if let Some(node) = cur { + let node = node.borrow(); + let left_state = Self::traversal(&node.left, ans); + let right_state = Self::traversal(&node.right, ans); + match (left_state, right_state) { + (NodeState::NoCover, _) | (_, NodeState::NoCover) => { + *ans += 1; + NodeState::Camera + } + (NodeState::Camera, _) | (_, NodeState::Camera) => NodeState::Covered, + (_, _) => NodeState::NoCover, + } + } else { + NodeState::Covered + } + } +} + ```

diff --git a/problems/1143.最长公共子序列.md b/problems/1143.最长公共子序列.md index b655b5cd..f2e6e7e2 100644 --- a/problems/1143.最长公共子序列.md +++ b/problems/1143.最长公共子序列.md @@ -49,7 +49,7 @@ dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符 有同学会问:为什么要定义长度为[0, i - 1]的字符串text1,定义为长度为[0, i]的字符串text1不香么? -这样定义是为了后面代码实现方便,如果非要定义为为长度为[0, i]的字符串text1也可以,我在 [动态规划:718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html) 中的「拓展」里 详细讲解了区别所在,其实就是简化了dp数组第一行和第一列的初始化逻辑。 +这样定义是为了后面代码实现方便,如果非要定义为长度为[0, i]的字符串text1也可以,我在 [动态规划:718. 最长重复子数组](https://programmercarl.com/0718.最长重复子数组.html) 中的「拓展」里 详细讲解了区别所在,其实就是简化了dp数组第一行和第一列的初始化逻辑。 2. 确定递推公式 @@ -240,27 +240,6 @@ func max(a,b int)int { ``` -Rust: -```rust -pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { - let (n, m) = (text1.len(), text2.len()); - let (s1, s2) = (text1.as_bytes(), text2.as_bytes()); - let mut dp = vec![0; m + 1]; - let mut last = vec![0; m + 1]; - for i in 1..=n { - dp.swap_with_slice(&mut last); - for j in 1..=m { - dp[j] = if s1[i - 1] == s2[j - 1] { - last[j - 1] + 1 - } else { - last[j].max(dp[j - 1]) - }; - } - } - dp[m] -} -``` - Javascript: ```javascript const longestCommonSubsequence = (text1, text2) => { @@ -304,6 +283,26 @@ function longestCommonSubsequence(text1: string, text2: string): number { }; ``` +Rust: +```rust +pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let (n, m) = (text1.len(), text2.len()); + let (s1, s2) = (text1.as_bytes(), text2.as_bytes()); + let mut dp = vec![0; m + 1]; + let mut last = vec![0; m + 1]; + for i in 1..=n { + dp.swap_with_slice(&mut last); + for j in 1..=m { + dp[j] = if s1[i - 1] == s2[j - 1] { + last[j - 1] + 1 + } else { + last[j].max(dp[j - 1]) + }; + } + } + dp[m] +} +```