mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-08 00:43:04 +08:00
Merge pull request #1841 from juguagua/leetcode-modify-the-code-of-the-dp
更新动态规划部分:从 “买卖股票的最佳时机IV” 到 “最长公共子序列”
This commit is contained in:
@ -323,40 +323,6 @@ func max(a, b int) int {
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func maxProfit(k int, prices []int) int {
|
||||
if len(prices)==0{
|
||||
return 0
|
||||
}
|
||||
dp:=make([][]int,len(prices))
|
||||
for i:=0;i<len(prices);i++{
|
||||
dp[i]=make([]int,2*k+1)
|
||||
}
|
||||
for i:=1;i<len(dp[0]);i++{
|
||||
if i%2!=0{
|
||||
dp[0][i]=-prices[0]
|
||||
}
|
||||
}
|
||||
for i:=1;i<len(prices);i++{
|
||||
dp[i][0]=dp[i-1][0]
|
||||
for j:=1;j<len(dp[0]);j++{
|
||||
if j%2!=0{
|
||||
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-prices[i])
|
||||
}else {
|
||||
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+prices[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[len(prices)-1][2*k]
|
||||
}
|
||||
func max(a,b int)int{
|
||||
if a>b{
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
```
|
||||
|
||||
Javascript:
|
||||
|
||||
```javascript
|
||||
|
@ -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>) -> 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>) -> 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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user