NC刷题笔记10-动态规划
-
- BM62 斐波那契数列
- BM63 跳台阶
- BM64 最小花费爬楼梯
- BM65 最长公共子序列(二)
- BM66 最长公共子串
- BM67 不同路径的数目(一)
- BM68 矩阵的最小路径和
- BM69 把数字翻译成字符串
- BM70 兑换零钱(一)
- BM71 最长上升子序列(一)
- BM72 连续子数组的最大和
- BM73 最长回文子串
- BM74 数字字符串转化成IP地址
- BM75 编辑距离(一)
- BM76 正则表达式匹配
- BM77 最长的括号子串
- BM78 打家劫舍(一)
- BM79 打家劫舍(二)
- BM80 买卖股票的最好时机(一)
- BM81 买卖股票的最好时机(二)
- BM82 买卖股票的最好时机(三)
BM62 斐波那契数列
思路:递归非递归
public class Solution { public int Fibonacci(int n) { if(n<=2) return 1; return Fibonacci(n-1)+Fibonacci(n-2); }}
public class Solution { public int Fibonacci(int n) { if(n<=2) return 1; int a=1,b=1,sum=a+b; for(int i=3;i<=n;i++){ sum=a+b; a=b; b=sum; } return sum; }}
BM63 跳台阶
描述一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。数据范围:1≤n≤40要求:时间复杂度:O(n) ,空间复杂度: O(1)示例1输入:2返回值:2说明:青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2示例2输入:7返回值:21
public class Solution { public int jumpFloor(int target) { if(target<=1) return 1; int a=1,b=1,sum=a+b; for(int i=2;i<=target;i++){ sum=a+b; a=b; b=sum; } return sum; }}
BM64 最小花费爬楼梯
描述给定一个整数数组 cost ,其中 cost[i]是从楼梯第i个台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。数据范围:数组长度满足 1≤n≤10^5,数组中的值满足 1≤cost≤10^4 示例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 。
import java.util. ;public class Solution { public int minCostClimbingStairs (int[] cost) { int n=cost.length; if(n==1||n==2) return 0; int[] dp=new int[n+1]; dp[0]=0; dp[1]=0; for(int i=2;i<=n;i++){ dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]); } return dp[n]; }}
BM65 最长公共子序列(二)
描述给定两个字符串str1和str2,输出两个字符串的最长公共子序列。如果最长公共子序列为空,则返回"-1"。目前给出的数据,仅仅会存在一个最长的公共子序列数据范围:0≤∣str1∣,∣str2∣≤2000要求:空间复杂度 O(n^2),时间复杂度 O(n^2)示例1输入:"1A2C3D4B56","B1D23A456A"返回值:"123456"示例2输入:"abc","def"返回值:"-1"示例3输入:"abc","abc"返回值:"abc"示例4输入:"ab",""返回值:"-1"
import java.util. ;public class Solution { public String LCS (String s1, String s2) { int n=s1.length(); int m=s2.length(); String[][] dp=new String[n+1][m+1]; for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ if(i==0||j==0){ dp[i][j]=""; }else if(s1.charAt(i-1)==s2.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]+s1.charAt(i-1); }else{ dp[i][j]=dp[i-1][j].length()>dp[i][j-1].length()?dp[i-1][j]:dp[i][j-1]; } } } return dp[n][m]==""?"-1":dp[n][m]; }}
BM66 最长公共子串
描述给定两个字符串str1和str2,输出两个字符串的最长公共子串题目保证str1和str2的最长公共子串存在且唯一。 数据范围:1≤∣str1∣,∣str2∣≤5000要求: 空间复杂度 O(n^2),时间复杂度 O(n^2)示例1输入:"1AB2345CD","12345EF"返回值:"2345"备注:1≤∣str1∣,∣str2∣≤5000
思路:动态规划某位置上相等: dp[i + 1][j + 1] = dp[i][j] + 1;某位置上不等: dp[i + 1][j+1] = 0;
public String LCS(String str1, String str2) { int maxLenth = 0; int maxLastIndex = 0; int[][] dp = new int[str1.length() + 1][str2.length() + 1]; for (int i = 0; i < str1.length(); i++) { for (int j = 0; j < str2.length(); j++) { if (str1.charAt(i) == str2.charAt(j)) { dp[i + 1][j + 1] = dp[i][j] + 1; if (dp[i + 1][j + 1] > maxLenth) { maxLenth = dp[i + 1][j+1]; maxLastIndex = i; } } else { dp[i + 1][j+1] = 0; } } } return str1.substring(maxLastIndex - maxLenth + 1, maxLastIndex + 1);}
BM67 不同路径的数目(一)
描述一个机器人在m×n大小的地图的左上角(起点)。机器人每次可以向下或向右移动。机器人要到达地图的右下角(终点)。可以有多少种不同的路径从起点走到终点?备注:m和n小于等于100,并保证计算结果在int范围内 数据范围:0<n,m≤100,保证计算结果在32位整型范围内要求:空间复杂度O(nm),时间复杂度O(nm)进阶:空间复杂度O(1),时间复杂度O(min(n,m))示例1输入:2,1返回值:1示例2输入:2,2返回值:2
思路:动态规划:dp[i][j]=dp[i-1][j]+dp[i][j-1];每个位置只会从左边或者上边过来
import java.util. ;public class Solution { public int uniquePaths (int m, int n) { int[][] dp=new int[m+1][n+1]; for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(i==1||j==1){ dp[i][j]=1; }else{ dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } } return dp[m][n]; }}
BM68 矩阵的最小路径和
描述给定一个 n m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。数据范围: 1≤n,m≤500,矩阵中任意值都满足0≤ai,j≤100要求:时间复杂度 O(nm)例如:当输入[[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 上边只能从左边过来2 左边只能从上边过来3 其他的可以从上左两个方向过来
import java.util. ;public class Solution { public int minPathSum (int[][] matrix) { if(matrix==null||matrix.length==0) return 0; int n=matrix.length; int m=matrix[0].length; int[][] dp=new int[n+1][m+1]; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(i==1){ dp[i][j]=dp[i][j-1]+matrix[i-1][j-1]; }else if(j==1){ dp[i][j]=dp[i-1][j]+matrix[i-1][j-1]; }else{ dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+matrix[i-1][j-1]; } } } return dp[n][m]; }}
BM69 把数字翻译成字符串
描述有一种将字母编码成数字的方式:'a'->1, 'b->2', ... , 'z->26'。现在给一串数字,返回有多少种可能的译码结果数据范围:字符串长度满足0<n≤90进阶:空间复杂度O(n),时间复杂度O(n)示例1输入:"12"返回值:2说明:2种可能的译码结果(”ab” 或”l”) 示例2输入:"31717126241541717"返回值:192说明:192种可能的译码结果
动态规划:四种情况1 无法翻译 当前为0 前面的>2或前面也为02 只能组合翻译 当前为0 前面为1或23 只能单独翻译 当前>6 前面为0或者前面>24 可以组合翻译 dp[i]=dp[i-1]+dp[i-2]
import java.util. ;public class Solution { public int solve (String nums) { if(nums==null ||nums.length()==0) return 0; int[] dp = new int[nums.length()+1]; dp[0]=1; dp[1]=nums.charAt(0)=='0'?0:1; for(int i=2;i<dp.length;i++){ if(nums.charAt(i-1)=='0' && (nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2')){ return 0; }else if(nums.charAt(i-1)=='0'){ dp[i] = dp[i-2]; }else if(nums.charAt(i-2)=='0' || nums.charAt(i-2)>'2' || nums.charAt(i-2)=='2'&& nums.charAt(i-1)>'6' ){ dp[i] = dp[i-1]; }else{ dp[i] = dp[i-1]+dp[i-2]; } } return dp[nums.length()]; }}
BM70 兑换零钱(一)
描述给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。 如果无解,请返回-1.数据范围:数组大小满足0≤n≤10000 , 数组中每个数字都满足0<val≤10000,0≤aim≤5000要求:时间复杂度O(n×aim) ,空间复杂度O(aim)示例1输入:[5,2,3],20返回值:4示例2输入:[5,2,3],0返回值:0示例3输入:[3,5],2返回值:-1
思路:动态规划dp[i] = Math.min(dp[i],dp[i-arr[j]] + 1);
import java.util. ;public class Solution { public int minMoney (int[] arr, int aim) { int Max = aim + 1; int []dp = new int[aim + 1]; Arrays.fill(dp,Max); dp[0] = 0; for(int i = 1;i <= aim;i ++){ for(int j = 0;j < arr.length;j ++){ if(arr[j] <= i){ dp[i] = Math.min(dp[i],dp[i-arr[j]] + 1); } } } return dp[aim] > aim ? -1 : dp[aim]; }}
BM71 最长上升子序列(一)
描述给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度。所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 i 和 j 满足 i<j 且 arri≥arrj。数据范围: 0≤n≤1000要求:时间复杂度 O(n^2), 空间复杂度 O(n)示例1输入:[6,3,1,5,2,3,7]返回值:4说明:该数组最长上升子序列为 [1,2,3,7] ,长度为4
思路:动态规划遍历0到i-1位置的元素,如果有小于i位置的元素 则dp[i]=Math.max(dp[i],dp[j]+1);返回最大值即可
import java.util. ;public class Solution { public int LIS (int[] arr) { if(arr==null||arr.length==0) return 0; int maxLen=1; int[] dp=new int[arr.length]; Arrays.fill(dp,1); for(int i=1;i<dp.length;i++){ for(int j=i-1;j>=0;j--){ if(arr[i]>arr[j]){ dp[i]=Math.max(dp[i],dp[j]+1); } } maxLen=Math.max(maxLen,dp[i]); } return maxLen; }}
BM72 连续子数组的最大和
描述输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。数据范围:1<=n<=2 10^5 , −100<=a[i]<=100要求:时间复杂度为 O(n),空间复杂度为 O(n)进阶:时间复杂度为 O(n),空间复杂度为 O(1)示例1输入:[1,-2,3,10,-4,7,2,-5]返回值:18说明:经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18 示例2输入:[2]返回值:2示例3输入:[-10]返回值:-10
思路1:动态规划 dp[i]=array[i-1]+((dp[i-1]>0)?dp[i-1]:0);时间复杂度O(n) 空间复杂度 O(n)思路2:思路1的改进,dp状态只需要1个,前一个元素的dp值
public class Solution { public int FindGreatestSumOfSubArray(int[] array) { if(array==null || array.length==0) return 0; int[] dp=new int[array.length+1]; int maxres=Integer.MIN_VALUE; for(int i=1;i<=array.length;i++){ dp[i]=array[i-1]+((dp[i-1]>0)?dp[i-1]:0); maxres=Math.max(maxres,dp[i]); } return maxres; }}
public class Solution { public int FindGreatestSumOfSubArray(int[] array) { if(array==null || array.length==0) return 0; int b=0; int maxSum=Integer.MIN_VALUE; for(int i=0;i<array.length;i++){ b=array[i]+(b>0?b:0); maxSum=Math.max(b,maxSum); } return maxSum; }}
BM73 最长回文子串
描述对于长度为n的一个字符串A(仅包含数字,大小写英文字母),请设计一个高效算法,计算其中最长回文子串的长度。数据范围:1≤n≤1000要求:空间复杂度 O(1),时间复杂度 O(n^2)进阶: 空间复杂度 O(n),时间复杂度 O(n)示例1输入:"ababc"返回值:3说明:最长的回文子串为"aba"与"bab",长度都为3示例2输入:"abbba"返回值:5示例3输入:"b"返回值:1
思路1:中心扩散法,需要判断 双数和单数两种情况时间复杂度:O(N^2),空间复杂度:O(1)思路2:双层for循环+双指针时间复杂度:O(N^2),空间复杂度:O(1)思路3:动态规划两层for循环dp[left][right] = dp[left + 1][right - 1];时间复杂度:O(N^2),空间复杂度:O(N^2)
import java.util. ;public class Solution { public int getLongestPalindrome(String A) { char[] chars=A.toCharArray(); int maxLength=0; for(int i=0;i<chars.length;i++){ maxLength=Math.max(maxLength,getLength(i,chars)); } return maxLength; } public int getLength(int i,char[] chars){ int res=1,left=i-1,right=i+1; while(left>=0 && right<chars.length){ if(chars[left]==chars[right]){ res+=2; left--; right++; }else{ break; } } int res0=0; if((i+1)<chars.length && chars[i]==chars[i+1]){ left=i; right=i+1; while(left>=0 && right<chars.length){ if(chars[left]==chars[right]){ res0+=2; left--; right++; }else{ break; }} } return Math.max(res,res0); }}
import java.util. ;public class Solution { public int getLongestPalindrome(String A) { char[] chars=A.toCharArray(); int maxlength=0; for(int i=0;i<chars.length;i++){ for(int j=i;j<chars.length;j++){ if(judge(i,j,chars)) maxlength=Math.max(maxlength,j-i+1); } } return maxlength; } public boolean judge(int i,int j,char[] chars){ if(i==j) return true; while(i<j){ if(chars[i]!=chars[j]) return false; i++; j--; } return true; }}
public int getLongestPalindrome(String A, int n) { if (n < 2) return A.length(); int maxLen = 1; boolean[][] dp = new boolean[n][n]; for (int right = 1; right < n; right++) { for (int left = 0; left <= right; left++) { if (A.charAt(left) != A.charAt(right)) continue; if (right == left) { dp[left][right] = true; } else if (right - left <= 2) { dp[left][right] = true; } else { dp[left][right] = dp[left + 1][right - 1]; } if (dp[left][right] && right - left + 1 > maxLen) { maxLen = right - left + 1; } } } return maxLen;}
BM74 数字字符串转化成IP地址
描述现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。例如:给出的字符串为"25525522135",返回["255.255.22.135", "255.255.221.35"]. (顺序没有关系)数据范围:字符串长度 0≤n≤12 要求:空间复杂度O(n!),时间复杂度 O(n!)注意: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"返回值:"[]"
思路1:暴力枚举,每一个位包含一到三位字符如果四个位置相加为s.length思路2:递归回溯
思路1:import java.util. ;public class Solution { public ArrayList<String> restoreIpAddresses (String s) { ArrayList<String> list = new ArrayList(); for(int a=1; a<4; a++){ for(int b=1; b<4; b++){ for(int c=1; c<4; c++){ for(int d=1; d<4; d++){ if(a+b+c+d==s.length()){String s1 = s.substring(0, a);String s2 = s.substring(a, a+b);String s3 = s.substring(a+b, a+b+c);String s4 = s.substring(a+b+c, a+b+c+d);if(check(s1)&&check(s2)&&check(s3)&&check(s4)){ String ip = s1+"."+s2+"."+s3+"."+s4; list.add(ip);} } } } } } return list; } public boolean check(String s){ if(Integer.valueOf(s)<=255){ if(s.charAt(0)!='0' || s.charAt(0)=='0'&&s.length()==1) return true; } return false; }}
思路2:import java.util. ;public class Solution { public ArrayList<String> restoreIpAddresses (String s) { if (s.length() > 12) { return null; } StringBuilder sb = new StringBuilder(s); doRestoreIpAddresses(sb, 1); return ans; } ArrayList<String> ans = new ArrayList<>(); public void doRestoreIpAddresses(StringBuilder s, int m) { String[] nums = s.toString().split("\\."); if (nums.length > 4) { return; } if (validateIPv4(s.toString())) { ans.add(String.copyValueOf(s.toString().toCharArray())); return; } for (int i = m; i < s.length(); i++) { s.insert(i, "."); doRestoreIpAddresses(s, i + 2); s.replace(i, i + 1, ""); } } public boolean validateIPv4(String IP) { String[] nums = IP.split("\\."); if (nums.length != 4) { return false; } for (String x : nums) { if (x.length() == 0 || x.length() > 3) return false; if (x.charAt(0) == '0' && x.length() != 1) return false; if (Integer.parseInt(x) > 255) return false; } return true; }}
BM75 编辑距离(一)
描述给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数。你可以对字符串进行3种操作:1.插入一个字符2.删除一个字符3.修改一个字符。字符串长度满足1≤n≤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
思路:动态规划 int[][] dp=new int[str1.length()+1][str2.length()+1]; 当前位置字符相等 dp[i+1][j+1]=dp[i][j]; 当前位置字符不相等 dp[i+1][j+1]=Math.min(insert,delete,replace)int delete=dp[i+1][j]+1; int insert=dp[i][j+1]+1; int change=dp[i][j]+1;
import java.util. ;public class Solution { public int editDistance (String str1, String str2) { if(str1.length()==0||str2.length()==0) return str1.length()==0?str2.length():str1.length(); int[][] dp=new int[str1.length()+1][str2.length()+1]; for(int i=0;i<=str1.length();i++){ dp[i][0]=i; } for(int i=0;i<=str2.length();i++){ dp[0][i]=i; } for(int i=0;i<str1.length();i++){ for(int j=0;j<str2.length();j++){ if(str1.charAt(i)==str2.charAt(j)){ dp[i+1][j+1]=dp[i][j]; }else{ int delete=dp[i+1][j]+1; int insert=dp[i][j+1]+1; int change=dp[i][j]+1; int min = change > insert ? insert : change; min = delete > min ? min : delete; dp[i + 1][j + 1] = min; } } } return dp[str1.length()][str2.length()]; }}
BM76 正则表达式匹配
描述请实现一个函数用来匹配包括'.'和' '的正则表达式。1.模式中的字符'.'表示任意一个字符2.模式中的字符' '表示它前面的字符可以出现任意次(包含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab ac a"匹配,但是与"aa.a"和"ab a"均不匹配数据范围:1.str 只包含从 a-z 的小写字母。2.pattern 只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 ' '。3. 0≤str.length≤26 4. 0≤pattern.length≤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
思路:动态规划1、 i和j处的字符相等 dp[i][j]=d[i-1][j-1]2、 j处为'.' dp[i][j]=d[i-1][j-1]3、 j处为' ' j-1处不为'.' 匹配成功3.1 匹配0次 dp[i][j]=dp[i][j-2] 3.2 匹配1次 dp[i][j]=d[i][j-1] 3.3 匹配n次 d[i][j]=d[i-1][j] 匹配不成功 3.4 匹配0次 dp[i][j]=dp[i][j-2] 4、j-1处为'.' j处为' '4.1 匹配0次 dp[i][j]=dp[i][j-2] 4.2 匹配1次 dp[i][j]=d[i][j-1] 4.3 匹配n次 d[i][j]=d[i-1][j]
import java.util. ;public class Solution { public boolean match (String str, String pattern) { boolean[][] dp=new boolean[str.length()+1][pattern.length()+1]; dp[0][0]=true; for(int i=1;i<=pattern.length();i++){ if (i >= 2 && pattern.charAt(i - 1) == ' ') dp[0][i] = dp[0][i - 2]; } for(int i=1;i<=str.length();i++){ for(int j=1;j<=pattern.length();j++){ if(str.charAt(i-1)==pattern.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]; }else if(pattern.charAt(j-1)=='.'){ dp[i][j]=dp[i-1][j-1]; }else if(pattern.charAt(j-1)==' '){ if(j>=2 && pattern.charAt(j-2)=='.'){ dp[i][j]=dp[i][j-2]||dp[i][j-1]||dp[i-1][j]; }else{ if(j>=2 && str.charAt(i-1)==pattern.charAt(j-2)){dp[i][j]=dp[i][j-2]||dp[i][j-1]||dp[i-1][j]; }else{dp[i][j]=dp[i][j-2]; } } } } } return dp[str.length()][pattern.length()]; }}
BM77 最长的括号子串
描述给出一个长度为 n 的,仅包含字符 '(' 和 ')' 的字符串,计算最长的格式正确的括号子串的长度。例1: 对于字符串 "(()" 来说,最长的格式正确的子串是 "()" ,长度为 2 .例2:对于字符串 ")()())" , 来说, 最长的格式正确的子串是 "()()" ,长度为 4 .字符串长度:0≤n≤5∗10 5要求时间复杂度 O(n) ,空间复杂度 O(n).示例1输入:"(()"返回值:2示例2输入:"(())"返回值:4
思路:动态规划当前位置i为( dp为0当前位置i为) 需要判断1、i-1为( ,dp[i]=dp[i-2]+22、i-1不为(,i-dp[i-1]-1=='(' dp[i]=dp[i-dp[i-1]-2]+dp[i-1]+2
import java.util. ;public class Solution { public int longestValidParentheses (String s) { int maxans = 0; int[] dp = new int[s.length()]; for (int i = 1; i < s.length(); i++) { if (s.charAt(i) == ')') { if (s.charAt(i - 1) == '(') { dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; } maxans = Math.max(maxans, dp[i]); } } return maxans; }}
BM78 打家劫舍(一)
描述你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。给定一个整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。数据范围:数组长度满足1≤n≤2×10^5,数组中每个值满足1≤num[i]≤5000 示例1输入:[1,2,3,4]返回值:6说明:最优方案是偷第 2,4 个房间 示例2输入:[1,3,6]返回值:7说明:最优方案是偷第 1,3个房间 示例3输入:[2,10,5]返回值:10说明:最优方案是偷第 2 个房间
思路:动态规划每天有两种状态 偷和不偷偷 dp[i-1][1]+nums[i-1]; 左边不偷+今天不偷 dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]); 昨天投和不偷的最大值
import java.util. ;public class Solution { public int rob (int[] nums) { int[][] dp=new int[nums.length+1][2]; dp[0][0]=0; dp[0][1]=0; int max=0; for(int i=1;i<=nums.length;i++){ dp[i][0]=dp[i-1][1]+nums[i-1]; dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]); max=Math.max(dp[i][0],dp[i][1]); } return max; }}
BM79 打家劫舍(二)
描述你是一个经验丰富的小偷,准备偷沿湖的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。给定一个长度为n的整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。数据范围:数组长度满足1≤n≤2×10^5,数组中每个值满足1≤nums[i]≤5000 示例1输入:[1,2,3,4]返回值:6说明:最优方案是偷第 2 4 个房间 示例2输入:[1,3,6]返回值:6说明:由于 1 和 3 是相邻的,因此最优方案是偷第 3 个房间
动态规划1 从1-(n-1)2 从2-n计算两边动态规划
import java.util. ;public class Solution { public int rob (int[] nums) { int[][] dp=new int[nums.length][2]; dp[0][0]=0; dp[0][1]=0; int RobFirst=0; for(int i=1;i<nums.length;i++){ dp[i][0]=dp[i-1][1]+nums[i-1]; dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]); RobFirst=Math.max(dp[i][0],dp[i][1]); } int noRobFirst=0; dp=new int[nums.length][2]; for(int i=1;i<nums.length;i++){ dp[i][0]=dp[i-1][1]+nums[i]; dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]); noRobFirst=Math.max(dp[i][0],dp[i][1]); } return Math.max(RobFirst,noRobFirst); }}
BM80 买卖股票的最好时机(一)
描述假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天2.如果不能获取到任何利润,请返回03.假设买入卖出均无手续费数据范围:0≤n≤10^5,0≤val≤10^4要求:空间复杂度 O(1),时间复杂度 O(n)示例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
思路:动态规划今天持有=max(今天买入,昨天就持有)今天不持有=max(昨天没有,昨天有今天卖出)
import java.util. ;public class Solution { public int maxProfit (int[] prices) { int[][] dp=new int[prices.length][2]; dp[0][0]=0; dp[0][1]=-prices[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], -prices[i]); } return dp[prices.length-1][0]; }}
import java.util. ;public class Solution { public int maxProfit (int[] prices) { if(prices.length<2) return 0; int max=0; for(int i=0;i<prices.length-1;i++){ int n1=prices[i]; for(int j=i+1;j<prices.length;j++){ max=Math.max(max,prices[j]-n1); } } return max; }}
BM81 买卖股票的最好时机(二)
描述假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票2. 如果不能获取收益,请返回03. 假设买入卖出均无手续费数据范围:1≤n≤1×10^5,1≤prices[i]≤10^4要求:空间复杂度 O(n),时间复杂度 O(n)进阶:空间复杂度 O(1),时间复杂度 O(n)示例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]范围内。
思路:动态规划今天持有=max(昨天利润+今天买入,昨天就持有)今天不持有=max(昨天没有,昨天有今天卖出)
import java.util. ;public class Solution { public int maxProfit (int[] prices) { int[][] dp=new int[prices.length][2]; dp[0][0]=0; dp[0][1]=-prices[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][0]; }}
BM82 买卖股票的最好时机(三)
描述假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票2. 如果不能获取收益,请返回03. 假设买入卖出均无手续费数据范围:1≤n≤10^5,股票的价格满足1≤val≤10^4要求: 空间复杂度 O(n),时间复杂度 O(n)进阶:空间复杂度 O(1),时间复杂度 O(n)示例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]范围内。
思路:5个状态:1)不操作2)第一次购买3)第一次卖出4)第二次购买5)第二次卖出
import java.util. ;public class Solution { public int maxProfit (int[] prices) { if (prices.length == 0) return 0; / 5个状态:1)不操作2)第一次购买3)第一次卖出4)第二次购买5)第二次卖出 dp[i][j]代表第i天状态为j时产生的最大收益 / int [][]dp = new int[prices.length][5]; dp[0][1] = -prices[0]; dp[0][3] = -prices[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], dp[i - 1][0] - 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]; }}
英语听力