牛客经典101题题解集--动态规划

目录

动态规划

斐波那契数列

题目

分析

代码

java

python

跳台阶

题目

代码

java

python

最小花费爬楼梯

题目

分析

代码

java

python

最长公共子序列(二)

题目

分析

代码

java

python

最长公共子串

题目

分析

代码

java

python

不同路径的数目(一)

题目

代码

java

python

矩阵的最小路径和

题目

代码

java

python

把数字翻译成字符串

题目

分析

代码

java

python

兑换零钱(一)

题目

代码

java

python

最长上升子序列(一)

题目

分析

代码

java

python

连续子数组的最大和

题目

分析

代码

java

python

最长回文字串

题目

分析

代码

java

python

数字字符串转化为IP地址

题目

代码

java

python

编辑距离(一)

题目

分析

代码

java

python

正则表达式匹配(11)

题目

分析

代码

java

python

最长的括号字串

题目

分析

代码

java

python

打家劫舍(一)

题目

代码

java

python

打家劫舍(二)

题目

分析

代码

java

python

买股票的最好时机(一)

题目

分析

代码

java

python

买股票的最好时机(二)

题目

代码

java

python

买股票的最好时机(三)

题目

分析

代码

java

python


动态规划

斐波那契数列

题目

大家都知道斐波那契数列,现在要求输入一个正整数 n ,请你输出斐波那契数列的第 n 项。

斐波那契数列是一个满足 𝑓𝑖𝑏(𝑥)={1𝑥=1,2𝑓𝑖𝑏(𝑥−1)+𝑓𝑖𝑏(𝑥−2)𝑥>2fib(x)={1fib(x−1)+fib(x−2)​x=1,2x>2​ 的数列

数据范围:1≤𝑛≤40

要求:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛),本题也有时间复杂度 𝑂(𝑙𝑜𝑔𝑛)的解法

输入描述:

一个正整数n

返回值描述:

输出一个正整数。

示例1

输入:4

返回值:3

说明:根据斐波那契数列的定义可知,fib(1)=1,fib(2)=1,fib(3)=fib(3-1)+fib(3-2)=2,fib(4)=fib(4-1)+fib(4-2)=3,所以答案为3。

示例2

输入:1

返回值:1

示例3

输入:2

返回值:

复制代码
1

分析

入门。相当于当前这个是由他前面两个数推导出来的,所以要知道1和2的初始化为多少,然后计算。

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @return int整型
     */
    public int Fibonacci (int n) {
        // write code here
        int [] dp=new int[n];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n-1];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param n int整型 
# @return int整型
#
class Solution:
    def Fibonacci(self , n: int) -> int:
        # write code here
        dp=[1]*n
        for i in range(2,n):
            dp[i]=dp[i-1]+dp[i-2]
        return dp[n-1]

跳台阶

题目

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

数据范围:1≤𝑛≤40

要求:时间复杂度:𝑂(𝑛),空间复杂度: 𝑂(1)

示例1

输入:2

返回值:2

说明:青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2

示例2

输入:7

返回值:21

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param number int整型 
     * @return int整型
     */
    public int jumpFloor (int number) {
        // write code here
        int [] dp=new int[number+1];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=number;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[number];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param number int整型 
# @return int整型
#
class Solution:
    def jumpFloor(self , number: int) -> int:
        # write code here
        dp=[0]*(number+1)
        dp[0]=1
        dp[1]=1
        for i in range(2,number+1):
            dp[i]=dp[i-1]+dp[i-2]
        return dp[number]

最小花费爬楼梯

题目

给定一个整数数组 𝑐𝑜𝑠𝑡,其中 𝑐𝑜𝑠𝑡[𝑖] 是从楼梯第𝑖 个台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

数据范围:数组长度满足 1≤𝑛≤105 ,数组中的值满足 1≤𝑐𝑜𝑠𝑡𝑖≤104

示例1

输入:[2,5,20]

返回值:5

说明:你将从下标为1的台阶开始,支付5 ,向上爬两个台阶,到达楼梯顶部。总花费为5

示例2

输入:[1,100,1,1,1,90,1,1,80,1]

返回值:6

说明:

复制代码
你将从下标为 0 的台阶开始。
1.支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
2.支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
3.支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
4.支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
5.支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
6.支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。    

分析

dp[i]所表示的含义是到达第i个台阶所花费的最小费用,所以他说可以从第0个开始或者第一个开始,dp[0]和dp[1]的初始化为0,然后到达第i个台阶可以是到达前一个台阶总花费+那个台阶的消耗或者前两个台阶总花费---+那个台阶的消耗,取最小值

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param cost int整型一维数组 
     * @return int整型
     */
    public int minCostClimbingStairs (int[] cost) {
        // write code here
        int [] dp=new int[cost.length+1];
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<=cost.length;i++){
            dp[i]=Math.min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]);
        }
        return dp[cost.length];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param cost int整型一维数组 
# @return int整型
#
class Solution:
    def minCostClimbingStairs(self , cost: List[int]) -> int:
        # write code here
        dp=[0]*(len(cost)+1)
        for i in range(2,len(cost)+1):
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
        return dp[len(cost)]

最长公共子序列(二)

题目

给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列

数据范围:0≤∣𝑠𝑡𝑟1∣,∣𝑠𝑡𝑟2∣≤2000

要求:空间复杂度 𝑂(𝑛2),时间复杂度 𝑂(𝑛2)

示例1

输入:"1A2C3D4B56","B1D23A456A"

返回值:"123456"

示例2

输入:"abc","def"

返回值:"-1"

示例3

输入:"abc","abc"

返回值:"abc"

示例4

输入:"ab",""

返回值:"-1"

分析

dp[i][j]用来表示到s1的i-1位置和s2的j-1位置的最长公共子序列,当i-1和j-1位置上字母一样的时候,直接在前一个的基础上+1,不一样的时候看舍弃哪个会更长一点

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    public String LCS (String s1, String s2) {
        // write code here
        int [][] dp=new int [s1.length()+1][s2.length()+1];
        dp[0][0]=0;
        for(int i=1;i<=s1.length();i++){
            for(int j=1;j<=s2.length();j++){
                if(s1.charAt(i-1)==s2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        if(dp[s1.length()][s2.length()]==0){
            return "-1";
        }
        StringBuilder sb=new StringBuilder();
        for(int i=s1.length(),j=s2.length();i>0 && j>0;){
            if(s1.charAt(i-1)==s2.charAt(j-1)){
                sb.append(s1.charAt(i-1));
                i--;
                j--;
            }else if(dp[i-1][j]>dp[i][j-1]){
                i--;
            }else{
                j--;
            }
        }
        return sb.reverse().toString();
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# longest common subsequence
# @param s1 string字符串 the string
# @param s2 string字符串 the string
# @return string字符串
#
class Solution:
    def LCS(self , s1: str, s2: str) -> str:
        # write code here
        m=len(s1)
        n=len(s2)
        dp=[[0] * (n+1) for _ in range(m+1)]
        for i in range(1,m+1):
            for j in range(1,n+1):
                if s1[i-1]==s2[j-1]:
                    dp[i][j]=dp[i-1][j-1]+1
                else:
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1])
        if dp[m][n]==0:
            return "-1"
        res=[]
        i, j = m, n
        while i > 0 and j > 0:
            if s1[i-1] == s2[j-1]:
                res.append(s1[i-1])
                i -= 1
                j -= 1
            elif dp[i-1][j] > dp[i][j-1]:
                i -= 1
            else:
                j -= 1
        return ''.join(reversed(res))

最长公共子串

题目

给定两个字符串str1和str2,输出两个字符串的最长公共子串

题目保证str1和str2的最长公共子串存在且唯一。

数据范围: 1≤∣𝑠𝑡𝑟1∣,∣𝑠𝑡𝑟2∣≤5000

要求: 空间复杂度 𝑂(𝑛2),时间复杂度 𝑂(𝑛2)

示例1

输入:"1AB2345CD","12345EF"

返回值:"2345"

备注:1≤∣𝑠𝑡𝑟1∣,∣𝑠𝑡𝑟2∣≤5 0001≤∣str1​∣,∣str2​∣≤5000

分析

这道题和上一道题不一样的地方在于,这个公共子串必须是连续的,就是说下标必须连续。

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    public String LCS (String str1, String str2) {
        // write code here
        int n=str1.length();
        int m=str2.length();
        int [][] dp=new int[n+1][m+1];
        dp[0][0]=0;
        int max=0;
        int index=0;
        for(int i=1;i<=str1.length();i++){
            for(int j=1;j<=str2.length();j++){
                if(str1.charAt(i-1)==str2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1]+1;
                 if(dp[i][j]>max){
                    max=dp[i][j];
                    index=i;
                 }
                }else{
                    dp[i][j]=0;
                }
            }
        }
        if(max == 0) return "-1";
        return str1.substring(index-max, index);
        
    }
}
python
python 复制代码
class Solution:
    def LCS(self, str1: str, str2: str) -> str:
        max_len = 0
        res = ""
        # 枚举所有可能的子串,利用 Python 字符串 in 优化(底层C速度)
        for i in range(len(str1)):
            for j in range(i + max_len + 1, len(str1) + 1):
                substr = str1[i:j]
                if substr in str2:
                    max_len = j - i
                    res = substr
                else:
                    break
        return res if res else "-1"

不同路径的数目(一)

题目

一个机器人在m×n大小的地图的左上角(起点)。

机器人每次可以向下或向右移动。机器人要到达地图的右下角(终点)。

可以有多少种不同的路径从起点走到终点?

备注:m和n小于等于100,并保证计算结果在int范围内

数据范围:0<𝑛,𝑚≤100,保证计算结果在32位整型范围内

要求:空间复杂度 𝑂(𝑛𝑚),时间复杂度 𝑂(𝑛𝑚)

进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑚𝑖𝑛(𝑛,𝑚))

示例1

输入:2,1

返回值:1

示例2

输入:2,2

返回值:2

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param m int整型 
     * @param n int整型 
     * @return int整型
     */
    public int uniquePaths (int m, int n) {
        // write code here
        int [][]dp=new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            dp[i][1]=1;
        }
        for(int j=1;j<=n;j++){
            dp[1][j]=1;
        }
        for(int i=2;i<=m;i++){
            for(int j=2;j<=n;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
return dp[m][n];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param m int整型 
# @param n int整型 
# @return int整型
#
class Solution:
    def uniquePaths(self , m: int, n: int) -> int:
        # write code here
        dp=[[0] * n for _ in range(m)]
        for i in range(m):
            dp[i][0]=1
        for j in range(n):
            dp[0][j]=1
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j]=dp[i-1][j]+dp[i][j-1]
        return dp[m-1][n-1]

矩阵的最小路径和

题目

给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

数据范围: 1≤𝑛,𝑚≤500,矩阵中任意值都满足 0≤𝑎𝑖,𝑗≤100

要求:时间复杂度 𝑂(𝑛𝑚)

例如:当输入[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]时,对应的返回值为12,

所选择的最小累加和路径如下图所示:

示例1

输入:[[1,3,5,9],[8,1,3,4],[5,0,6,1],[8,8,4,0]]

返回值:12

示例2

输入:[[1,2,3],[1,2,3]]

返回值:7

备注:

1≤𝑛,𝑚≤20001≤n,m≤2000

1≤𝑎𝑖,𝑗≤1001≤ai,j​≤100

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param matrix int整型二维数组 the matrix
     * @return int整型
     */
    public int minPathSum (int[][] matrix) {
        // write code here
        int n=matrix.length;
        int m=matrix[0].length;
        int [][] dp=new int[n][m];
        dp[0][0]=matrix[0][0];
        for(int i=1;i<n;i++){
            dp[i][0]=dp[i-1][0]+matrix[i][0];
        }
        for(int j=1;j<m;j++){
            dp[0][j]=dp[0][j-1]+matrix[0][j];
        }
        for(int i=1;i<n;i++){
            for(int j=1;j<m;j++){
                dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+matrix[i][j];
            }
        }
        return dp[n-1][m-1];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param matrix int整型二维数组 the matrix
# @return int整型
#
class Solution:
    def minPathSum(self , matrix: List[List[int]]) -> int:
        # write code here
        n=len(matrix)
        m=len(matrix[0])
        dp=[[0] * m for _ in range(n)]
        dp[0][0]=matrix[0][0]
        for i in range(1,n):
            dp[i][0]=dp[i-1][0]+matrix[i][0]
        for j in range(1,m):
            dp[0][j]=dp[0][j-1]+matrix[0][j]
        for i in range(1,n):
            for j in range(1,m):
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+matrix[i][j]
        return dp[n-1][m-1]
        

把数字翻译成字符串

题目

有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。

现在给一串数字,返回有多少种可能的译码结果

数据范围:字符串长度满足 0<𝑛≤90

进阶:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)

示例1

输入:"12"

返回值:2

说明:2种可能的译码结果("ab" 或"l")

示例2

输入:"31717126241541717"

返回值:192

说明:192种可能的译码结果

分析

dp表示译码的方法。

首先初始化,把特殊情况现在前面讨论了,然后第一个数字的方法只有1种,然后带上第2位,如果第一位和第二位组合在一起小于26且第二位不是0的话,那么译码方法是2,不然就是1,为什么要单独说一下第二位是不是0呢?因为如果是0的话,第二位是不能单独译码的,智能和第一位一起,所以只有一种译码方法

分为以下几种情况:

1.当前数字是0,但是前一位是1或者2,那么当前必须和之前一位一起译码,相当于把他们两个绑定了,所以是dp[i]=dp[i-2](要往前走两位)(而且一定要记住dp数组的含义是译码方法,所以不加一)

2.如果当前数字小于等于6并且前一位的数字是1或者2,那么就是正常的,dp[i]=dp[i-1]+dp[i-2],不

3.其他情况 不能组合,只能单独解码,dp[i]=dp[i-1]

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 解码
     * @param nums string字符串 数字串
     * @return int整型
     */
    public int solve (String nums) {
        // write code here
        if(nums.length() == 0 || nums.charAt(0) == '0') return 0;
        if(nums.length()==1) return 1;
        int [] dp=new int[nums.length()];
        dp[0]=1;
        int two= (nums.charAt(0)-'0')*10 + (nums.charAt(1)-'0');
        dp[1]=(two>=1 && two<=26 && nums.charAt(1) != '0')?2:1;
        for(int i=2;i<nums.length();i++){
            if(nums.charAt(i)=='0'){
                if(nums.charAt(i-1)=='1' || nums.charAt(i-1)=='2'){
                    dp[i]=dp[i-2];
                }else{
                    dp[i]=0;
                }
            }else if(nums.charAt(i-1)=='1' || nums.charAt(i-1)=='2' && nums.charAt(i)<='6'){
                dp[i]=dp[i-1]+dp[i-2];
            }else{
                dp[i]=dp[i-1];
            }
        }
        return dp[nums.length()-1];

    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 解码
# @param nums string字符串 数字串
# @return int整型
#
class Solution:
    def solve(self , nums: str) -> int:
        # write code here
        if len(nums)==0 or nums[0]=='0':
            return 0
        n=len(nums)
        if n==1:
            return 1
        dp=[0]*n
        dp[0]=1
        two = int(nums[0]) * 10 + int(nums[1])
        dp[1]=2 if(two<=26 and nums[1]!='0') else 1
        for i in range(2,n):
            if nums[i]=='0':
                if nums[i-1]=='1' or nums[i-1]=='2':
                    dp[i]=dp[i-2]
                else:
                    dp[i]=0
            elif nums[i-1]=='1' or nums[i-1]=='2' and nums[i]<='6':
                dp[i]=dp[i-1]+dp[i-2]
            else:
                dp[i]=dp[i-1]
        return dp[n-1]

兑换零钱(一)

题目

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。

如果无解,请返回-1.

数据范围:数组大小满足 0≤𝑛≤10000, 数组中每个数字都满足 0<𝑣𝑎𝑙≤10000,0≤𝑎𝑖𝑚≤5000

要求:时间复杂度 𝑂(𝑛×𝑎𝑖𝑚) ,空间复杂度 𝑂(𝑎𝑖𝑚)。

示例1

输入:[5,2,3],20

返回值:4

示例2

输入:[5,2,3],0

返回值:0

示例3

输入:[3,5],2

返回值:-1

备注:0≤𝑛≤10 0000≤n≤10000 0≤𝑎𝑖𝑚≤5 0000≤aim≤5000

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public int minMoney (int[] arr, int aim) {
        // write code here
        int [][] dp=new int[arr.length][aim+1];
         if(aim == 0) return 0;
        if(arr.length==0){
            return -1;
        }
        for(int i=0;i<=aim;i++){
            dp[0][i]=aim+1;
        }
        for(int i=arr[0];i<=aim;i+=arr[0]){
            dp[0][i]=i/arr[0];
        }
        dp[0][0]=0;
        for(int i=1;i<arr.length;i++){
            for(int j=0;j<=aim;j++){
                dp[i][j]=dp[i-1][j];
                if(j>=arr[i] && dp[i][j-arr[i]]!=aim+1){
                    dp[i][j]=Math.min(dp[i][j],dp[i][j-arr[i]]+1);
                }
            }
        }
        return dp[arr.length-1][aim] == aim+1 ? -1 : dp[arr.length-1][aim];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 最少货币数
# @param arr int整型一维数组 the array
# @param aim int整型 the target
# @return int整型
#
class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        # write code here
        if aim == 0:
            return 0
        if not arr:
            return -1
        a=aim+1
        dp=[[a] * (aim+1) for _ in range(len(arr))]
        dp[0][0]=0
        for i in range(arr[0],aim+1,arr[0]):
            dp[0][i]=i//arr[0]
        
        for i in range(1,len(arr)):
            for j in range(aim+1):
                dp[i][j] = dp[i-1][j]  # 不选当前硬币
                if j >= arr[i] and dp[i][j - arr[i]] != a:
                    dp[i][j] = min(dp[i][j], dp[i][j - arr[i]] + 1)
        
        return dp[-1][aim] if dp[-1][aim] != a else -1

最长上升子序列(一)

题目

给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。

所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。

我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 𝑖i 和 𝑗j 满足 𝑖<𝑗i<j 且 𝑎𝑟𝑟𝑖≥𝑎𝑟𝑟𝑗arri​≥arrj​。

数据范围: 0≤𝑛≤1000

要求:时间复杂度 𝑂(𝑛2), 空间复杂度 𝑂(𝑛)

示例1

输入:[6,3,1,5,2,3,7]

返回值:4

说明:该数组最长上升子序列为 [1,2,3,7] ,长度为4

分析

每一个单独的数字都可以看作是上升子序列,所以初始化都为1

dp[i] 的定义是:以第 i 个数结尾的最长严格上升子序列长度

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        // write code here
        if(arr.length==1){
            return 1;
        }
        if(arr.length==0){
            return 0;
        }
        int res=0;
        int [] dp=new int[arr.length];
        Arrays.fill(dp,1);
        for(int i=1;i<arr.length;i++){
            for(int j=0;j<i;j++){
                if(arr[j]<arr[i]){
                    dp[i]=Math.max(dp[j]+1,dp[i]);
                }
            }res=Math.max(res,dp[i]);
        }
        return res;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 给定数组的最长严格上升子序列的长度。
# @param arr int整型一维数组 给定的数组
# @return int整型
#
class Solution:
    def LIS(self , arr: List[int]) -> int:
        # write code here
        if len(arr)==0:
            return 0
        if len(arr)==1:
            return 1
        dp=[1]*len(arr)
        result=0
        for i in range(1,len(arr)):
            for j in range(i):
                if arr[j]<arr[i]:
                    dp[i]=max(dp[j]+1,dp[i])
            result=max(result,dp[i])
        return result

连续子数组的最大和

题目

输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。

数据范围:

1<=𝑛<=2×105

−100<=𝑎[𝑖]<=100

要求:时间复杂度为 𝑂(𝑛),空间复杂度为 𝑂(𝑛)

进阶:时间复杂度为 𝑂(𝑛),空间复杂度为 𝑂(1)

示例1

输入:[1,-2,3,10,-4,7,2,-5]

返回值:18

说明:经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18

示例2

输入:[2]

返回值:2

示例3

输入:[-10]

返回值:-10

分析

当累计的和小于1 的时候,再继续加下去只会使得后面的数字更小,所以我们可以从这里断开,重新开始

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型一维数组 
     * @return int整型
     */
    public int FindGreatestSumOfSubArray (int[] array) {
        // write code here
        if(array.length==0){
            return 0;
        }
        if(array.length==1){
            return array[0];
        }
        int [] dp=new int[array.length];
        dp[0]=array[0];
        int result=array[0];
        for(int i=1;i<array.length;i++){
            dp[i]=Math.max(array[i],dp[i-1]+array[i]);
            result=Math.max(result,dp[i]);
        }
        
        return result;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param array int整型一维数组 
# @return int整型
#
class Solution:
    def FindGreatestSumOfSubArray(self , array: List[int]) -> int:
        # write code here
        dp=[0]*len(array)
        dp[0]=array[0]
        result=dp[0]
        for i in range(1,len(array)):
            dp[i]=max(dp[i-1]+array[i],array[i])
            result=max(result,dp[i])
        return result

最长回文字串

题目

对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度。

数据范围: 1≤𝑛≤1000

要求:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛2)

进阶: 空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)

示例1

输入:"ababc"

返回值:3

说明:最长的回文子串为"aba"与"bab",长度都为3

示例2

输入:"abbba"

返回值:5

示例3

输入:"b"

返回值:1

分析

dp[i][j]:表示:字符串从下标 i 到 j 的这一段,是不是回文

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param A string字符串 
     * @return int整型
     */
    public int getLongestPalindrome (String A) {
        // write code here
        int n=A.length();
        int result=0;
        boolean [][] dp=new boolean[n][n];
        for(int i=n-1;i>=0;i--){
            for(int j=i;j<n;j++){
                if(A.charAt(i)==A.charAt(j)){
                    if(j-i<=1){
                        dp[i][j]=true;
                    }else if(dp[i+1][j-1]){
                        dp[i][j]=true;
                    }
                }
                if(dp[i][j]){
                    result=Math.max(result,j-i+1);
                }
            }
        }
        return result;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param A string字符串 
# @return int整型
#
class Solution:
    def getLongestPalindrome(self , A: str) -> int:
        # write code here
        n=len(A)
        dp=[[False] * n for _ in range(n)]
        result=0
        for i in range(n-1,-1,-1):
            for j in range(i,n):
                if A[i]==A[j]:
                    if j-i<=1:
                        dp[i][j]=True
                    elif dp[i+1][j-1]:
                        dp[i][j]=True
                if dp[i][j]:
                    result=max(result,j-i+1)
        return result

数字字符串转化为IP地址

题目

现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。

例如:

给出的字符串为"25525522135",

返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)

数据范围:字符串长度 0≤𝑛≤12

要求:空间复杂度 𝑂(𝑛!),时间复杂度 𝑂(𝑛!)

注意:ip地址是由四段数字组成的数字序列,格式如 "x.x.x.x",其中 x 的范围应当是 [0,255]。

示例1

输入:"25525522135"

返回值:["255.255.22.135","255.255.221.35"]

示例2

输入:"1111"

返回值:["1.1.1.1"]

示例3

输入:"000256"

返回值:[]

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return string字符串ArrayList
     */
     ArrayList<String> result=new ArrayList<>();
    public ArrayList<String> restoreIpAddresses (String s) {
        // write code here
        int n=s.length();
        if(n<=3 || n>12){
            return result;
        }
        back(s,0,0);
        return result;
    }
    public void back(String s,int start,int num){
        if(num==3){
        if(isValid(s,start,s.length()-1)){
            result.add(s);
        }
        return;
        }
        for(int i=start;i<s.length();i++){
            if(isValid(s,start,i)){
                s=s.substring(0,i+1)+'.'+s.substring(i+1);
                num++;
                back(s,i+2,num);
                num--;
                s = s.substring(0, i + 1) + s.substring(i + 2);
            }else{
                break;
            }
        }
    }
    public boolean isValid(String s,int start,int end){
        if(end<start){
            return false;
        }
        if(s.charAt(start)=='0' && start!=end){
            return false;
        }
        int num=0;
        for(int i=start;i<=end;i++){
            if(s.charAt(i)>'9' || s.charAt(i)<'0'){
                return false;
            }
            num=num*10+(s.charAt(i)-'0');
            if(num>255){
                return false;
            }
        }
        return true;
    }

}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# @param s string字符串 
# @return string字符串一维数组
#
from typing import List

class Solution:
    def __init__(self):
        self.result=[]
        
    def restoreIpAddresses(self , s: str) -> List[str]:
        # write code here
        if len(s)<4 or len(s)>12:
            return self.result
        self.back(s,0,0)
        return self.result
    
    def back(self,s,start,sum):
        if sum == 3:
            if self.isValid(s, start, len(s)-1):
                self.result.append(s)
            return
        
        # 循环切分
        for i in range(start, len(s)):
            if self.isValid(s, start, i):
                # 插入小数点
                new_s = s[:i+1] + "." + s[i+1:]
                sum += 1
                self.back(new_s, i+2, sum)
                # 回溯
                sum -= 1
            else:
                break

    def isValid(self,s,start,end):
        if start>end:
            return False
        if s[start]=='0' and end!=start:
            return False
        
        num = 0
        for i in range(start,end+1):
            if not s[i].isdigit():
                return False
            num = num * 10 + int(s[i])
            if num > 255:
                return False
        return True

编辑距离(一)

题目

给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数。

你可以对字符串进行3种操作:

1.插入一个字符

2.删除一个字符

3.修改一个字符。

字符串长度满足 1≤𝑛≤1000 ,保证字符串中只出现小写英文字母。

示例1

输入:"nowcoder","new"

返回值:6

说明:"nowcoder"=>"newcoder"(将'o'替换为'e'),修改操作1次 "nowcoder"=>"new"(删除"coder"),删除操作5次

示例2

输入:"intention","execution"

返回值:5

说明:一种方案为: 因为2个长度都是9,后面的4个后缀的长度都为"tion",于是从"inten"到"execu"逐个修改即可

示例3

输入:"now","nowcoder"

返回值:5

分析

dp[i-1][j] + 1

代表:删除 str1 的最后一个字符

dp[i][j-1] + 1

代表:在 str1 末尾插入一个字符

dp[i-1][j-1] + 1

代表:把 str1 的字符替换成 str2 的字符

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param str1 string字符串 
     * @param str2 string字符串 
     * @return int整型
     */
    public int editDistance (String str1, String str2) {
        // write code here
        int n=str1.length();
        int m=str2.length();
        int [][] dp=new int[n+1][m+1];
        for(int i=0;i<=n;i++){
            dp[i][0]=i;
        }
        for(int j=0;j<=m;j++){
            dp[0][j]=j;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(str1.charAt(i-1)==str2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1];
                }else{
                    dp[i][j]=Math.min(Math.min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);
                }
            }
        }
    return dp[n][m];
    }
}
python
python 复制代码
from re import I
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param str1 string字符串 
# @param str2 string字符串 
# @return int整型
#
class Solution:
    def editDistance(self , str1: str, str2: str) -> int:
        # write code here
        n=len(str1)
        m=len(str2)
        dp=[[0] * (m+1) for _ in range(n+1)]
        for i in range(n+1):
            dp[i][0]=i
        for j in range(m+1):
            dp[0][j]=j
        for i in range(1,n+1):
            for j in range(1,m+1):
                if str1[i-1]==str2[j-1]:
                    dp[i][j]=dp[i-1][j-1]
                else:
                    dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1)
        return dp[n][m]

正则表达式匹配(11)

题目

请实现一个函数用来匹配包括'.'和'*'的正则表达式。

1.模式中的字符'.'表示任意一个字符

2.模式中的字符'*'表示它前面的字符可以出现任意次(包含0次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

数据范围:

1.str 只包含从 a-z 的小写字母。

2.pattern 只包含从 a-z 的小写字母以及字符 . 和 *,无连续的 '*'。

  1. 0≤𝑠𝑡𝑟.𝑙𝑒𝑛𝑔𝑡ℎ≤26

  2. 0≤𝑝𝑎𝑡𝑡𝑒𝑟𝑛.𝑙𝑒𝑛𝑔𝑡ℎ≤26

示例1

输入:"aaa","a*a"

返回值:true

说明:中间的*可以出现任意次的a,所以可以出现1次a,能匹配上

示例2

输入:"aad","c*a*d"

返回值:true

说明:因为这里 c 为 0 个,a被重复一次, * 表示零个或多个a。因此可以匹配字符串 "aad"。

示例3

输入:"a",".*"

返回值:true

说明:".*" 表示可匹配零个或多个('*')任意字符('.')

示例4

输入:"aaab","a*a*a*c"

返回值:false

分析

match1 = False

if str[i-1] == pattern[j-2] or pattern[j-2] == '.':

match1 = dp[i-1][j]

这里的意思是*可以匹配多次,那么我就一直缩小str,看*前面的数字是否可以持续匹配上,也就是匹配多次的情况

代码

java
java 复制代码
import java.util.*;

public class Solution {
    public boolean match (String str, String pattern) {
        int n = str.length();
        int m = pattern.length();
        boolean[][] dp = new boolean[n+1][m+1];
        
        // 空串匹配空串 为真
        dp[0][0] = true;
        
        // 处理:str为空,pattern是 a* / a*b* 这种情况(必须初始化)
        for (int j = 1; j <= m; j++) {
            if (pattern.charAt(j-1) == '*') {
                dp[0][j] = dp[0][j-2];
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                // 情况1:当前不是 * → 必须和前一个状态同时满足
                if (pattern.charAt(j-1) != '*') {
                    if (dp[i-1][j-1] && 
                        (str.charAt(i-1) == pattern.charAt(j-1) || pattern.charAt(j-1) == '.')) {
                        dp[i][j] = true;
                    }
                } 
                // 情况2:当前是 * → 核心难点
                else {
                    // 两种大情况:* 匹配 0 次  OR  匹配 至少1次
                    
                    // ① 匹配 0 次:直接忽略 x*
                    boolean match0 = dp[i][j-2];
                    
                    // ② 匹配 至少1次:前面字符匹配,且 i-1 位置合法
                    boolean match1 = false;
                    if (str.charAt(i-1) == pattern.charAt(j-2) || pattern.charAt(j-2) == '.') {
                        match1 = dp[i-1][j];
                    }
                    
                    dp[i][j] = match0 || match1;
                }
            }
        }
        return dp[n][m];
    }
}
python
python 复制代码
class Solution:
    def match(self , str: str, pattern: str) -> bool:
        n = len(str)
        m = len(pattern)
        
        # dp[i][j] 表示 str前i个字符 和 pattern前j个字符 是否匹配
        dp = [[False] * (m + 1) for _ in range(n + 1)]
        
        # 空串匹配空串,一定成功
        dp[0][0] = True
        
        # 处理:str为空,pattern是 a* / .* / a*b* 这种情况
        for j in range(1, m + 1):
            if pattern[j-1] == '*':
                dp[0][j] = dp[0][j-2]
        
        # 开始填表
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                # 情况1:当前不是 *
                if pattern[j-1] != '*':
                    # 字符相等 或者 是. 并且 前面匹配
                    if dp[i-1][j-1] and (str[i-1] == pattern[j-1] or pattern[j-1] == '.'):
                        dp[i][j] = True
                
                # 情况2:当前是 * ------ 核心!
                else:
                    # 选择1:* 匹配 0 次,直接忽略 x*
                    match0 = dp[i][j-2]
                    
                    # 选择2:* 匹配至少 1 次
                    match1 = False
                    if str[i-1] == pattern[j-2] or pattern[j-2] == '.':
                        match1 = dp[i-1][j]
                    
                    # 两种选择只要一个成立就行
                    dp[i][j] = match0 or match1
        
        return dp[n][m]

最长的括号字串

题目

给出一个长度为 n 的,仅包含字符 '(' 和 ')' 的字符串,计算最长的格式正确的括号子串的长度。

例1: 对于字符串 "(()" 来说,最长的格式正确的子串是 "()" ,长度为 2 .

例2:对于字符串 ")()())" , 来说, 最长的格式正确的子串是 "()()" ,长度为 4 .

字符串长度:0≤𝑛≤5∗105

要求时间复杂度 𝑂(𝑛),空间复杂度 𝑂(𝑛)

示例1

输入:"(()"

返回值:2

示例2

输入:"(())"

返回值:4

分析

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return int整型
     */
    public int longestValidParentheses (String s) {
        // write code here
        int n=s.length();
        int [] dp=new int[n];
        int result=0;
        for(int i=0;i<s.length();i++){
            if(i>0 && s.charAt(i)==')'){
                if(s.charAt(i-1)=='('){
                    dp[i]=(i-2>=0?dp[i-2]+2:2);
                }else if(s.charAt(i-1)==')' && i-dp[i-1]-1>=0 && s.charAt(i-dp[i-1]-1)=='('){
                    dp[i]=dp[i-1]+2+(i-dp[i-1]-2>=0?dp[i-dp[i-1]-2]:0);
                }
            }
            result=Math.max(result,dp[i]);
        }
        return result;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param s string字符串 
# @return int整型
#
class Solution:
    def longestValidParentheses(self , s: str) -> int:
        # write code here
        n=len(s)
        dp=[0]*n
        res=0
        for i in range(n):
            if i>0 and s[i]==')':
                if s[i-1]=='(':
                    dp[i]=dp[i-2]+2 if i >= 2 else 2
                elif s[i-1]==')' and i-dp[i-1]-1>=0 and s[i-dp[i-1]-1]=='(':
                    dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]//它所对应的左括号的左边的位置
                if dp[i]>res:
                    res=dp[i]
        return res

打家劫舍(一)

题目

你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。

给定一个整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。

数据范围:数组长度满足 1≤𝑛≤2×105 ,数组中每个值满足 1≤𝑛𝑢𝑚[𝑖]≤5000

示例1

输入:[1,2,3,4]

返回值:6

说明:最优方案是偷第 2,4 个房间

示例2

输入:[1,3,6]

返回值:7

说明:最优方案是偷第 1,3个房间

示例3

输入:[2,10,5]

返回值:10

说明:最优方案是偷第 2 个房间

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int rob (int[] nums) {
        // write code here
        if(nums.length==1){
            return nums[0];
        }
        int [] dp=new int[nums.length];
        dp[0]=nums[0];
        dp[1]=Math.max(dp[0],nums[1]);
        for(int i=2;i<nums.length;i++){
            dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[nums.length-1];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def rob(self , nums: List[int]) -> int:
        # write code here
        if len(nums)==1:
            return nums[0]
        dp=[0]*len(nums)
        dp[0]=nums[0]
        dp[1]=max(nums[0],nums[1])
        for i in range(2,len(nums)):
            dp[i]=max(dp[i-2]+nums[i],dp[i-1])
        return dp[len(nums)-1]

打家劫舍(二)

题目

你是一个经验丰富的小偷,准备偷沿湖的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。

给定一个长度为n的整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。

数据范围:数组长度满足 1≤𝑛≤2×105,数组中每个值满足 1≤𝑛𝑢𝑚𝑠[𝑖]≤5000

示例1

输入:[1,2,3,4]

返回值:6

说明:最优方案是偷第 2 4 个房间

示例2

输入:[1,3,6]

返回值:6

说明:由于 1 和 3 是相邻的,因此最优方案是偷第 3 个房间

分析

rob1中的函z相当于当前天最多投多少钱,y相当于前一天,x相当于后一天

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int rob (int[] nums) {
        // write code here
         if(nums.length==1){
            return nums[0];
        }
        if(nums.length==0){
            return 0;
        }
        return Math.max(rob1(nums,0,nums.length-1),rob1(nums,1,nums.length));
    }
    public int rob1(int [] nums,int start,int end){
        int x=0,y=0,z=0;
        for(int i=start;i<end;i++){
            y=z;
            z=Math.max(x+nums[i],y);
            x=y;
        }
        return z;
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param nums int整型一维数组 
# @return int整型
#
class Solution:
    def rob(self , nums: List[int]) -> int:
        # write code here
        if len(nums)==0:
            return 0
        if len(nums)==1:
            return nums[0]
        return max(self.rob1(nums,0,len(nums)-1),self.rob1(nums,1,len(nums)))
    def rob1(self,nums,start,end):
        x=0
        y=0
        z=0
        for i in range(start,end):
            y=z
            z=max(y,x+nums[i])
            x=y
        return z

买股票的最好时机(一)

题目

假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益

1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天

2.如果不能获取到任何利润,请返回0

3.假设买入卖出均无手续费

数据范围: 0≤𝑛≤105,0≤𝑣𝑎𝑙≤104

要求:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛)

示例1

输入:[8,9,2,5,4,7,1]

返回值:5

说明:在第3天(股票价格 = 2)的时候买入,在第6天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第2天买入,第3天卖出,这样就亏损7了;同时,你也不能在买入前卖出股票。

示例2

输入:[2,4,1]

返回值:2

示例3

输入:[3,2,1]

返回值:0

分析

dp[i][0]代表第i天持有股票

dp[i][1]代表第i天不持有股票

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param prices int整型一维数组 
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int [][] dp=new int[prices.length][2];
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],-prices[i]);
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
    return dp[prices.length-1][1];

    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param prices int整型一维数组 
# @return int整型
#
class Solution:
    def maxProfit(self , prices: List[int]) -> int:
        # write code here
        dp=[[0] * 2 for _ in range(len(prices))]
        dp[0][0]=-prices[0]
        dp[0][1]=0
        for i in range(1,len(prices)):
            dp[i][0]=max(dp[i-1][0],-prices[i])
            dp[i][1]=max(dp[i-1][0]+prices[i],dp[i-1][1])
        return dp[len(prices)-1][1]

买股票的最好时机(二)

题目

假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益

  1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票

  2. 如果不能获取收益,请返回0

  3. 假设买入卖出均无手续费

数据范围: 1≤𝑛≤1×105, 1≤𝑝𝑟𝑖𝑐𝑒𝑠[𝑖]≤104

要求:空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)

进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛)

示例1

输入:[8,9,2,5,4,7,1]

返回值:7

说明:在第1天(股票价格=8)买入,第2天(股票价格=9)卖出,获利9-8=1 在第3天(股票价格=2)买入,第4天(股票价格=5)卖出,获利5-2=3 在第5天(股票价格=4)买入,第6天(股票价格=7)卖出,获利7-4=3 总获利1+3+3=7,返回7

示例2

输入:[5,4,3,2,1]

返回值:0

说明:由于每天股票都在跌,因此不进行任何交易最优。最大收益为0。

示例3

输入:[1,2,3,4,5]

返回值:4

说明:第一天买进,最后一天卖出最优。中间的当天买进当天卖出不影响最终结果。最大收益为4。

备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 计算最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int [][] dp=new int[prices.length][2];
        dp[0][0]=-prices[0];
        dp[0][1]=0;
        for(int i=1;i<prices.length;i++){
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }
        return dp[prices.length-1][1];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 计算最大收益
# @param prices int整型一维数组 股票每一天的价格
# @return int整型
#
class Solution:
    def maxProfit(self , prices: List[int]) -> int:
        # write code here
        dp=[[0] * 2 for _ in range(len(prices))]
        dp[0][0]=-prices[0]
        dp[0][1]=0
        for i in range(1,len(prices)):
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])
            dp[i][1]=max(dp[i-1][0]+prices[i],dp[i-1][1])
        return dp[len(prices)-1][1]

买股票的最好时机(三)

题目

假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益

  1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票

  2. 如果不能获取收益,请返回0

  3. 假设买入卖出均无手续费

数据范围:1≤𝑛≤105,股票的价格满足 1≤𝑣𝑎𝑙≤104

要求: 空间复杂度 𝑂(𝑛),时间复杂度 𝑂(𝑛)

进阶:空间复杂度 𝑂(1),时间复杂度 𝑂(𝑛)

示例1

输入:[8,9,3,5,1,3]

返回值:4

说明:第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2 第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2 总收益为4。

示例2

输入:[9,8,4,1]

返回值:0

示例3

输入:[1,2,8,3,8]

返回值:12

说明:第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。 因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。

备注:总天数不大于200000。保证股票每一天的价格在[1,100]范围内。

分析

dp[0][0]=0;不操作

dp[0][1]=-prices[0];第一次操作持有

dp[0][2]=0;第一次操作不持有

dp[0][3]=-prices[0];第二次操作持有

dp[0][4]=0;第二次操作不持有

代码

java
java 复制代码
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 两次交易所能获得的最大收益
     * @param prices int整型一维数组 股票每一天的价格
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        int [][] dp=new int[prices.length][5];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        dp[0][2]=0;
        dp[0][3]=-prices[0];
        dp[0][4]=0;
        for(int i=1;i<prices.length;i++){
            dp[i][0]=dp[i-1][0];
            dp[i][1]=Math.max(dp[i-1][1],-prices[i]);
            dp[i][2]=Math.max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3]=Math.max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4]=Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[prices.length-1][4];
    }
}
python
python 复制代码
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 两次交易所能获得的最大收益
# @param prices int整型一维数组 股票每一天的价格
# @return int整型
#
class Solution:
    def maxProfit(self , prices: List[int]) -> int:
        # write code here
        dp=[[0] * 5 for _ in range(len(prices))]
        dp[0][0]=0
        dp[0][1]=-prices[0]
        dp[0][2]=0
        dp[0][3]=-prices[0]
        dp[0][4]=0
        for i in range(1,len(prices)):
            dp[i][0]=dp[i-1][0]
            dp[i][1]=max(dp[i-1][1],-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])
            dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i])
        return dp[len(prices)-1][4]
相关推荐
gmaajt2 小时前
Golang怎么做国际化多语言_Golang i18n教程【核心】
jvm·数据库·python
一次旅行2 小时前
IDEA安装CC GUI新手指南
java·ide·intellij-idea
超梦dasgg2 小时前
Spring AI 智能航空助手项目实战
java·人工智能·后端·spring·ai编程
maqr_1102 小时前
CSS如何利用Sass定义全局阴影方案_通过变量实现统一CSS风格
jvm·数据库·python
脱氧核糖核酸__2 小时前
LeetCode热题100——234.回文链表(两种解法)
c++·算法·leetcode·链表
IronMurphy2 小时前
【算法四十二】118. 杨辉三角 198. 打家劫舍
算法
m0_613856292 小时前
uni-app怎么做类似于美团的商家评价星级 uni-app五星评分组件制作【实战】
jvm·数据库·python
电科一班林耿超2 小时前
第 16 课:动态规划专题(二)—— 子序列与子数组问题:面试最高频的 DP 题型
数据结构·算法·动态规划
却道天凉_好个秋2 小时前
卷积神经网络CNN(七):感受野
人工智能·python·深度学习·神经网络·感受野