- 509斐波那契数
- 1137第N个泰波那契数
- 70爬楼梯
- 746使用最小花费爬楼梯
- 198打家劫舍
- 213打家劫舍II
- 740删除并获得点数
- 55跳跃游戏
- 45跳跃游戏II
- 53最大子数组和
- 918环形子数组的最大和
- 152乘积最大子数组
- 1567乘积为正数的最长子数组长度
- 1014最佳观光组合
- 121股票买卖最佳时机
- 122买卖股票的最佳时机II
- 309最佳买卖股票时机含冷冻期
- 714买卖股票的最佳时机含手续费
- 139单词拆分
- 42接雨水
- 413等差数列划分
- 91解码方法
- 264丑数II
- 96不同的二叉搜索树
- 118杨辉三角
- 119杨辉三角II
- 70爬楼梯
- 322零钱兑换
- 416分割等和子集
- 931下降路径最小和
- 120三角形最小路径和
- 62不同路径
- 63不同路径II
- 64最小路径和
- 221最大正方形
- 5最长回文串
- 516最长回文子序列
- 300最长递增子序列
- 376摆动序列
力扣学习计划——动态规划入门 所有题目解法包含暴力递归以及dp的改造
股票、组合、背包、回文序列、公共序列、路径、打家劫舍、跳跃游戏、零钱
509斐波那契数
// f(n) = f(n-1)+f(n-2)// f(0) = 0// f(1) = 1public int fib(int n) {if (n <= 1) return n;int[] dp = new int[n+1];dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i-1]+dp[i-2];}return dp[n];}private int run(int n){if (n <= 1){return n;}return run(n-1)+run(n-2);}
1137第N个泰波那契数
public int tribonacci(int n) {if (n == 0) return 0;if (n <= 2) return 1;int[] dp = new int[n+1];dp[1] = dp[2] = 1;for (int i = 3; i <= n; i++) {dp[i] = dp[i-1]+dp[i-2]+dp[i-3];}return dp[n];}private int run(int n){if (n == 0) return 0;if (n <= 2) return 1;return run(n-1)+run(n-2)+run(n-3);}
70爬楼梯
public int climbStairs(int n) {// return run(n,0);if (n <= 2) return n;int[] dp = new int[n+1];dp[n-1] = 1;dp[n-2] = 2;for (int i = n-3; i >=0; i--) {dp[i] = dp[i+1]+dp[i+2];}return dp[0];}// 从now到n有多少种方法private int run(int n,int now){if (now >= n){return 0;}if (now == n-1){return 1;}if (now == n-2){return 2;}int v1 = run(n,now+1);int v2 = run(n,now+2);return v1+v2;}
746使用最小花费爬楼梯
public int minCostClimbingStairs(int[] cost) {// return Math.min(run(cost,0),run(cost,1));int n = cost.length;int[] dp = new int[n+1];dp[n-1] = cost[n-1];dp[n-2] = cost[n-2];for (int i = n-3; i >=0; i--) {dp[i] = cost[i] + Math.min(dp[i+1],dp[i+2]);}return Math.min(dp[0],dp[1]);}//在index处爬到顶端的花费,target=cost.lengthprivate int run(int[] cost,int index){int n = cost.length;// 到顶了if (index == n){return 0;}// 距离顶端不超过两格,一步跨过去if (index >= n - 2){return cost[index];}// 跨一步int v1 = run(cost,index+1);// 跨两步int v2 = run(cost,index+2);// 当前价值+最小价值return cost[index] + Math.min(v1,v2);}
198打家劫舍
public int rob(int[] nums) {// return run(nums,nums.length-1);int n = nums.length;if (n == 1) return nums[0];int[] dp = new int[n];dp[0] = nums[0];dp[1] = Math.max(nums[0],nums[1]);for (int i = 2; i < n; i++) {// 偷index的,那么index-1不能偷,看到index-2的时候偷到了多少钱int v1 = nums[i] + dp[i-2];// 不偷index的,看index-1偷了多少钱就是当前偷了多少钱int v2 = dp[i-1];dp[i] = Math.max(v1,v2);}return dp[n-1];}// [0..index]时能偷到的最大金额private int run(int[] nums,int index){// 只有一个选择,肯定偷if (index == 0){return nums[0];}// 有两个,选最大的那个if(index == 1){return Math.max(nums[0],nums[1]);}// 偷index的,那么index-1不能偷,看到index-2的时候偷到了多少钱int v1 = nums[index] + run(nums,index-2);// 不偷index的,看index-1偷了多少钱就是当前偷了多少钱int v2 = run(nums,index-1);return Math.max(v1,v2);}
213打家劫舍II
public int rob(int[] nums) {int n = nums.length;if (n == 1) return nums[0];if (n == 2) return Math.max(nums[0],nums[1]);// return Math.max(run(nums,0,n-2),run(nums,1,n-1));return Math.max(dp(nums,0)[n-2],dp(nums,1)[n-1]);}private int[] dp(int[] nums,int start){int n = nums.length;int[] dp = new int[n];dp[start] = nums[start];dp[start+1] = Math.max(nums[start],nums[start+1]);for (int i = start+2; i <n; i++) {// 偷index的,那么index-1不能偷,看到index-2的时候偷到了多少钱int v1 = nums[i] + dp[i-2];// 不偷index的,看index-1偷了多少钱就是当前偷了多少钱int v2 = dp[i-1];dp[i] = Math.max(v1,v2);}return dp;}// [0..index]时能偷到的最大金额private int run(int[] nums,int start,int index){// 只有一个选择,肯定偷if (index == start){return nums[start];}// 有两个,选最大的那个if(index == start+1){return Math.max(nums[start],nums[start+1]);}// 偷index的,那么index-1不能偷,看到index-2的时候偷到了多少钱int v1 = nums[index] + run(nums,start,index-2);// 不偷index的,看index-1偷了多少钱就是当前偷了多少钱int v2 = run(nums,start,index-1);return Math.max(v1,v2);}
740删除并获得点数
public int deleteAndEarn(int[] nums) {if (nums.length == 1) return nums[0];Arrays.sort(nums);int[] array = new int[nums.length];int pre = nums[0], sum = nums[0] , arrIndex = 0, arrStart = 0;int ans = 0;for (int i = 1; i < nums.length; i++) {if (nums[i] == nums[i-1]){sum += nums[i];}else if (nums[i] == nums[i-1] + 1){array[arrIndex++] = sum;pre = nums[i];sum = nums[i];}else {array[arrIndex++] = sum;pre = nums[i];sum = nums[i];ans += dp(array,arrStart,arrIndex)[arrIndex-1];arrStart = arrIndex;}}array[arrIndex++] = sum;ans += dp(array,arrStart,arrIndex)[arrIndex-1];return ans;}private int[] dp(int[] nums,int start,int end){int n = nums.length;int[] dp = new int[n];dp[start] = nums[start];if (start+1 < end){dp[start+1] = Math.max(nums[start],nums[start+1]);// 拿当前的for (int i = start+2; i < end; i++) {int v1 = nums[i] + dp[i-2];int v2 = dp[i-1];dp[i] = Math.max(v1,v2);}}return dp;}public int deleteAndEarn2(int[] nums) {if (nums.length == 1) return nums[0];Arrays.sort(nums);int[] array = new int[nums.length];int pre = nums[0], sum = nums[0] , arrIndex = 0, arrStart = 0;int ans = 0;for (int i = 1; i < nums.length; i++) {if (nums[i] == nums[i-1]){sum += nums[i];}else if (nums[i] == nums[i-1] + 1){array[arrIndex++] = sum;pre = nums[i];sum = nums[i];}else {array[arrIndex++] = sum;pre = nums[i];sum = nums[i];ans += run(array,arrStart,arrIndex-1);arrStart = arrIndex;}}array[arrIndex++] = sum;ans += run(array,arrStart,arrIndex-1);return ans;}// nums: 值连续的数组,合并之后得到的新数组// index: 当前的nums的位置// [0..index]能拿到的最大值private int run(int[] nums,int start,int index){if (index < start){return 0;}if (index == start){return nums[start];}if(index == start+1){return Math.max(nums[start],nums[start+1]);}// 拿当前的int v1 = nums[index] + run(nums,start,index-2);int v2 = run(nums,start,index-1);return Math.max(v1,v2);}
55跳跃游戏
public boolean canJump(int[] nums) {// return run(nums,0);boolean[] dp = new boolean[nums.length];dp[nums.length-1] = true;for (int i = nums.length-2; i >= 0; i--) {int step = nums[i];if (nums[i] == 0) continue;boolean can = false;for (int j = 1; j <= step; j++) {can = can | (i+j >= nums.length-1 ? true : dp[i+j]);dp[i] = can;}}return dp[0];}// 从cur开始跳能不能跳上去private boolean run(int[] nums,int cur){// 当前位置大于等于最后位置的,都算能跳上来if (cur >= nums.length-1){return true;}int step = nums[cur];// 如果当前位置能跳0,肯定不行if (step == 0) return false;boolean can = false;for (int i = 1; i <= step; i++) {can |= run(nums,cur+i);if (can) return true;}return false;}
45跳跃游戏II
public int jump(int[] nums) {// return run(nums,0);int[] dp = new int[nums.length];int n = nums.length;// 显式设置一下base case,下面循环可以从n-2开始dp[n-1] = 0;for (int i = n-2; i >= 0; i--) {int step = nums[i];// 这里跳不上去了,返回个-1代表此路不通if (step == 0){dp[i] = -1;continue;}int min = Integer.MAX_VALUE;// 算下后面的最少需要多少次for (int j = 1; j <= step; j++) {int next = (i + j) >= n-1 ? 0 : dp[i+j];// -1的结果舍弃掉if (next >= 0){min = Math.min(next,min);}}dp[i] = min+1;}return dp[0];}// 从cur跳到顶部需要的最少步数private int run(int[] nums,int cur){int n = nums.length;// 已经到了顶端,还需要0次if (cur >= n-1){return 0;}int step = nums[cur];// 这里跳不上去了,返回个-1代表此路不通if (step == 0){return -1;}int min = Integer.MAX_VALUE;// 算下后面的最少需要多少次for (int i = 1; i <= step; i++) {int next = run(nums,cur+i);// -1的结果舍弃掉if (next >= 0){min = Math.min(next,min);}}return 1+min;}
53最大子数组和
public int maxSubArray(int[] nums) {int n = nums.length;if (n == 1) return nums[0];int[] dp = new int[n];dp[0] = nums[0];for (int i = 1; i < n; i++) {dp[i]= Math.max(nums[i],nums[i]+dp[i-1]);}return Arrays.stream(dp).max().getAsInt();}public int maxSubArray2(int[] nums) {if (nums.length == 1) return nums[0];max = nums[0];run(nums,nums.length-1);return max;}private int max = Integer.MIN_VALUE;// 截止到cur的最大连续子数组和// 注意拥有最大连续和的子数组并不一定是以nums[n-1]结尾的// curMax代表一定以cur为结尾的最大连续子数组和// max代表在cur之前但不一定以cur结尾的最大连续子数组和private int run(int[] nums,int cur){int curMax = 0;if (cur == 0){curMax = nums[0];}else {curMax= Math.max(nums[cur],nums[cur]+run(nums,cur-1));}max = Math.max(curMax,max);return curMax;}
918环形子数组的最大和
public int maxSubarraySumCircular(int[] nums) {if (nums.length == 1) return nums[0];int[] dp1 = new int[nums.length];int[] dp2 = new int[nums.length];dp1[0] = dp2[0] = nums[0];int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;for (int i = 1; i < nums.length; i++) {dp1[i] = Math.min(nums[i],dp1[i-1]+nums[i]);dp2[i] = Math.max(nums[i],dp2[i-1]+nums[i]);min = Math.min(dp1[i],min);max = Math.max(dp2[i],max);}int sum = Arrays.stream(nums).sum();// 防止全部都是负数的情况return sum == min ? max : Math.max(max, sum - min);}private int min = Integer.MAX_VALUE;private int max = Integer.MIN_VALUE;// 如果最大连续子数组位于数组中间,则正常求最大连续子数组和即可// 如果最大的分布在两端,则正常求最小连续子数组和,用总和减去最小和即可public int maxSubarraySumCircular2(int[] nums) {if (nums.length == 1) return nums[0];run1(nums,nums.length-1);run2(nums,nums.length-1);int sum = Arrays.stream(nums).sum();return sum == min ? max : Math.max(max, sum - min);}// 以cur结尾的连续数组最小和private int run1(int[] nums,int cur){int curMin = Integer.MAX_VALUE;if (cur == 0){curMin = nums[0];}else {curMin = Math.min(nums[cur],run1(nums,cur-1)+nums[cur]);}min = Math.min(min,curMin);return curMin;}// 以cur结尾的连续数组最大和private int run2(int[] nums,int cur){int curMax = Integer.MIN_VALUE;if (cur == 0){curMax = nums[0];}else if (cur == 1){curMax = Math.max(Math.max(nums[0],nums[1]),nums[0]+nums[1]);}else {curMax = Math.max(nums[cur],run2(nums,cur-1)+nums[cur]);}max = Math.max(max,curMax);return curMax;}
152乘积最大子数组
public int maxProduct(int[] nums) {if (nums.length == 1) return nums[0];int ans = nums[0];int[] max = new int[nums.length];int[] min = new int[nums.length];max[0] = min[0] = nums[0];for (int i = 1; i < nums.length; i++) {max[i] = Math.max(nums[i],Math.max(max[i-1]*nums[i],min[i-1]*nums[i]));min[i] = Math.min(nums[i],Math.min(max[i-1]*nums[i],min[i-1]*nums[i]));ans = Math.max(max[i],ans);}return ans;}public int maxProduct2(int[] nums) {run(nums,nums.length-1);min = max = nums[0];return max;}private Integer min;private Integer max;// 以index为结尾的连续数组的最大值和最小值private int[] run(int[] nums,int index){if (index == 0){return new int[]{nums[0],nums[0]};}int[] next = run(nums,index-1);// 当前index结尾的最大值要么是自身,要么是自身和index-1处的最大值的乘积,或者当前值是负的,最大值为当前值和index-1最小值乘积// 注意由于有负负得正的情况,next[0] > next[1] 不一定代表next[0]*nums[index]>next[1]*nums[index]int mx = Math.max(nums[index],Math.max(next[0]*nums[index],next[1]*nums[index]));int mn = Math.min(nums[index],Math.min(next[0] * nums[index],next[1] * nums[index]));max = Math.max(mx,max);min = Math.min(mn,min);return new int[]{mx,mn};}
1567乘积为正数的最长子数组长度
public int getMaxLen(int[] nums) {if (nums.length == 1) return nums[0] > 0 ? 1 : 0;int[][] dp = new int[nums.length][2];int[] start = nums[0] > 0 ? new int[]{1,0} : nums[0] == 0 ? new int[]{0,0} : new int[]{0,1};dp[0] = start;int ans = Integer.MIN_VALUE;for (int i = 1; i < nums.length; i++) {int[] next = dp[i-1];int[] cur = new int[2];if (nums[i] > 0){// 当前是正数,正数长度 = 原来正数长度直接+1,即使没有正数,自身也能构成长度为1的正数// 负数长度 = 如果原来有负数,该正数添上能让负数长度+1。但如果原来没用负数,该正数无法增加负数长度cur[0] = next[0] + 1;cur[1] = next[1] == 0 ? 0 : next[1]+1;}else if (nums[i] < 0){// 当前是负数,负数长度是原来正数长度+1,即使没有正数,自身也能构成长度为1的负数// 正数长度 = 如果原来有负数,长度+1。但如果原来没用负数,该负数无法增加正数长度cur[1] = next[0] + 1;cur[0] = next[1] == 0 ? 0 : next[1] + 1;}ans = Math.max(cur[0],ans);dp[i] = cur;}return ans;}public int getMaxLen2(int[] nums) {if (nums.length == 1) return nums[0] > 0 ? 1 : 0;int[] ans = run(nums,nums.length-1);return max;}// 以index为结尾的连续子数组// [0] index结尾乘积为正数的长度// [1] index结尾乘积为负数的长度private int max = 0;private int[] run(int[] nums,int index){if (index == 0){return nums[0] > 0 ? new int[]{1,0} : nums[0] == 0 ? new int[]{0,0} : new int[]{0,1};}int[] next = run(nums,index-1);int[] cur = new int[2];if (nums[index] > 0){// 当前是正数,正数长度 = 原来正数长度直接+1,即使没有正数,自身也能构成长度为1的正数// 负数长度 = 如果原来有负数,该正数添上能让负数长度+1。但如果原来没用负数,该正数无法增加负数长度cur[0] = next[0] + 1;cur[1] = next[1] == 0 ? 0 : next[1]+1;}else if (nums[index] < 0){// 当前是负数,负数长度是原来正数长度+1,即使没有正数,自身也能构成长度为1的负数// 正数长度 = 如果原来有负数,长度+1。但如果原来没用负数,该负数无法增加正数长度cur[1] = next[0] + 1;cur[0] = next[1] == 0 ? 0 : next[1] + 1;}max = Math.max(cur[0],max);return cur;}
1014最佳观光组合
public int maxScoreSightseeingPair2(int[] values) {int n = values.length;int[][] dp = new int[n][n];dp[0][1] = dp[1][0] = values[0] + values[1] -1;for (int i = 1; i < n -1; i++) {dp[i][i+1] = values[i] + values[i+1]-1;dp[i][i-1] = values[i] + values[i-1]-1;}dp[n -1][n -2] = values[n-1] + values[n-2] -1;// for (int i = 0; i < values.length; i++) {// for (int j = 0; j < values.length; j++) {// if (Math.abs(i - j) == 1){// dp[i][j] = values[i] + values[j] - 1;// }// }// }// dp[i][j] 与 左、下、左下和自身对比,因此画图可以看出遍历顺序// 优化下可以不用对比左下,因为左比左下大,下也比左下大for (int i = values.length-3; i >=0 ; i--) {for (int j = i+2; j < values.length; j++) {// v1 可以省略int v1 = dp[i+1][j-1];int v2 = dp[i][j-1];int v3 = dp[i+1][j];int v4 = values[i] + values[j] + i - j;dp[i][j] = Math.max(Math.max(v1,v2),Math.max(v3,v4));}}return dp[0][n -1];}public int maxScoreSightseeingPair2(int[] values) {return run(values,0,values.length-1);}// [start...end] 之间的最佳组合分数private int run(int[] values,int start,int end){if (start < 0 || end == values.length || start >= end){return 0;}if (Math.abs(start - end) == 1){return values[start]+values[end]-1;}// 一定不以start和end为两端// 可能以start为左端,一定不以end为右端// 可能以end为右端,一定不以start为左端// 一定以start和end为两端int v1 = run(values,start+1,end-1);int v2 = run(values,start,end-1);int v3 = run(values,start+1,end);int v4 = values[start] + values[end] + start - end;return Math.max(Math.max(v1,v2),Math.max(v3,v4));}
// 上面时间复杂度是O(n2),可以进一步优化// 对于公式value[i]+value[j]+i-j// 对于j的答案,即[0...j-1]与j组成的最大值即value[i]+i的最大值// j与后面组成的组合会包含在j取后面的值的情况中class Solution {public int maxScoreSightseeingPair(int[] values) {int ans = 0, mx = values[0] + 0;for (int j = 1; j < values.length; ++j) {ans = Math.max(ans, mx + values[j] - j);// 边遍历边维护mx = Math.max(mx, values[j] + j);}return ans;}}
121股票买卖最佳时机
public int maxProfit(int[] prices) {if (prices.length <= 1) return 0;int[][] dp = new int[prices.length][2];dp[0][0] = prices[0];for (int i = 1; i < prices.length; i++) {dp[i][1] = Math.max(dp[i-1][1],prices[i] - dp[i-1][0]);dp[i][0] = Math.min(dp[i-1][0],prices[i]);}return dp[prices.length-1][1];}public int maxProfit2(int[] prices) {if (prices.length <= 1) return 0;return run(prices,prices.length-1)[1];}// [0] 截止到index最低的买入价格// [1] index及之前能卖出的最大价格private int[] run(int[] prices,int index){if (index == 0){return new int[]{prices[0],0};}int[] next = run(prices,index-1);next[1] = Math.max(next[1],prices[index] - next[0]);next[0] = Math.min(next[0],prices[index]);return next;}
122买卖股票的最佳时机II
public int maxProfit(int[] prices) {if (prices.length <= 1) return 0;int[][]dp = new int[prices.length][2];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];}public int maxProfit2(int[] prices) {if (prices.length <= 1) return 0;return run(prices,prices.length-1)[0];}// 到index天// [0] 手里没股票的最大利润// [1] 手里有股票的最大利润private int[] run(int[] prices,int index){if (index == 0){return new int[]{0,-prices[0]};}int[] next = run(prices,index-1);// 没股票 = 之前就没股票或者之前有股票现在卖掉int v1 = Math.max(next[0],prices[index]+next[1]);// 有股票 = 之前有股票或者之前没股票现在买一只int v2 = Math.max(next[1],next[0]-prices[index]);return new int[]{v1,v2};}
309最佳买卖股票时机含冷冻期
public int maxProfit(int[] prices) {int[][] dp = new int[prices.length][3];dp[0][0] = -prices[0];for (int i = 1; i < prices.length; i++) {int[] next = dp[i-1];int v1 = Math.max(next[0],next[1]-prices[i]);// 不持有股票且不在冷冻期 = 前面 没有且不在冷冻期/不持有股票处于冷冻期利润int v2 = Math.max(next[1],next[2]);// 不持有股票处于冷冻期利润 = 前面有股票当前卖出int v3 = next[0] + prices[i];dp[i] = new int[]{v1,v2,v3};}return Math.max(dp[prices.length-1][1],dp[prices.length-1][2]);}public int maxProfit2(int[] prices) {int[] ans = run(prices,prices.length-1);return Math.max(ans[1],ans[2]);}// [0] 持有股票最大利润// [1] 不持有股票且不在冷冻期利润// [2] 当前不持有股票处于冷冻期利润private int[] run(int[] prices,int index){if (index == 0){return new int[]{-prices[0],0,0};}int[] next = run(prices,index-1);// 持有股票 = 之前没有当前买入/之前有int v1 = Math.max(next[0],next[1]-prices[index]);// 不持有股票且不在冷冻期 = 前面 没有且不在冷冻期/不持有股票处于冷冻期利润int v2 = Math.max(next[1],next[2]);// 不持有股票处于冷冻期利润 = 前面有股票当前卖出,这里状态是当index天结束时处于冷冻期,后面index+1不能买入int v3 = next[0] + prices[index];return new int[]{v1,v2,v3};}
714买卖股票的最佳时机含手续费
public int maxProfit(int[] prices, int fee) {if (prices.length <= 1) return 0;int[][] dp = new int[prices.length][2];dp[0][0] = -prices[0];for (int i = 1; i < prices.length; i++) {int[] next = dp[i-1];// 持有 = 前面持有无操作 | 前面不持有当前买入int v1 = Math.max(next[0],next[1] - prices[i]);// 不持有 = 前面不持有 | 前面持有卖出减去手续费int v2 = Math.max(next[1],next[0] + prices[i] - fee);dp[i] = new int[]{v1,v2};}return dp[prices.length-1][1];}public int maxProfit2(int[] prices, int fee) {if (prices.length <= 1) return 0;return run(prices,fee,prices.length-1)[1];}// [0] 持有// [1] 不持有private int[] run(int[] prices, int fee,int index){if (index == 0){return new int[]{-prices[0],0};}int[] next = run(prices,fee,index-1);// 持有 = 前面持有无操作 | 前面不持有当前买入int v1 = Math.max(next[0],next[1] - prices[index]);// 不持有 = 前面不持有 | 前面持有卖出减去手续费int v2 = Math.max(next[1],next[0] + prices[index] - fee);return new int[]{v1,v2};}
139单词拆分
public boolean wordBreak(String s, List<String> wordDict) {int n = s.length();Set<String> wd = new HashSet<>(wordDict.size());int max = Integer.MIN_VALUE;// 记录下字典里的最长长度for (String st : wordDict) {wd.add(st);max = Math.max(st.length(),max);}boolean[] dp = new boolean[n+1];char[] str = s.toCharArray();dp[n] = true;dp[n-1] = wd.contains(str[n-1]+"");for (int i = n-2; i >= 0; i--) {StringBuilder builder = new StringBuilder();for (int j = i; j < n; j++) {//长度比字典最长的词还长直接截断if (builder.length() > max){break;}builder.append(str[j]);if (wd.contains(builder.toString())){boolean ok = dp[j+1];if (ok){dp[i] = true;}}}}return dp[0];}public boolean wordBreak2(String s, List<String> wordDict) {Set<String> wd = new HashSet<>(wordDict);return run(s.toCharArray(),wd,0);}// str从start到结尾能否构成private boolean run(char[] str,Set<String> wd,int start){int n = str.length;// 能到达这里说明前面的都是符合条件递归进来的,所以直接返回trueif (start == n) return true;StringBuilder builder = new StringBuilder();for (int i = start; i < n; i++) {builder.append(str[i]);if (wd.contains(builder.toString())){boolean ok = run(str,wd,i+1);if (ok){return true;}}}return false;}
42接雨水
public int trap(int[] height) {int n = height.length;int[] leftMax = new int[n];int[] rightMax = new int[n];leftMax[0] = height[0];rightMax[n-1] = height[n-1];for (int i = 1; i < n; i++) {leftMax[i] = Math.max(leftMax[i-1],height[i]);}for (int i = n-2; i >=0; i--) {rightMax[i] = Math.max(rightMax[i+1],height[i]);}int ans = 0;for (int i = 0; i < n; i++) {ans += (Math.min(leftMax[i],rightMax[i]) - height[i]);}return ans;}
413等差数列划分
public int numberOfArithmeticSlices(int[] nums) {if (nums.length < 3) return 0;int ans = 0 , c = 0;int[] dp = new int[nums.length];for (int i = 2; i < nums.length; i++) {int next = dp[i-1];if (nums[i] - nums[i-1] == nums[i-1] - nums[i-2]){c = 1 + next;ans += c;dp[i] = c;}}return ans;}public int numberOfArithmeticSlices2(int[] nums) {if (nums.length < 3) return 0;run(nums,nums.length-1);return count;}private int count = 0;// 以end为结尾的等差数列的数量。private int run(int[] nums,int end){if (end < 2){return 0;}int c = 0;// 先判断前面的有几个int next = run(nums,end-1);// 如果end和前面的构成等差数列,除了新增一个end-2,end-1,end这个新等差数列// 前面end-1处的所有等差数列后面都加一个end,构成长度最小为4的新的等差数列if (nums[end] - nums[end-1] == nums[end-1] - nums[end-2]){c = 1 + next;count += c;}return c;}
91解码方法
public int numDecodings(String s) {int n = s.length();int[] dp = new int[n+1];char[] str = s.toCharArray();dp[n] = 1;for (int i = n-1; i >=0; i--) {if (str[i] == '0'){dp[i] = 0;continue;}//解码index,下一个就是index+1int v1 = dp[i+1];//解码index和index+1,但这个两位数不能超过26int v2 = 0;if (i+1 < str.length && ((str[i] - '0') * 10 + (str[i+1] - '0')) < 27){v2 = dp[i+2];}dp[i] = v1 + v2;}return dp[0];}public int numDecodings2(String s) {return run(s.toCharArray(),0);}//[index...n]有几种方式private int run(char[] str,int index){//到头了,当前路径有效,返回有效方式1if (index == str.length){return 1;}//如果当前位置数字是0,则肯定无效,直接返回0if (str[index] == '0'){return 0;}//解码index,下一个就是index+1int v1 = run(str,index+1);//解码index和index+1,但这个两位数不能超过26int v2 = 0;if (index+1 < str.length && ((str[index] - '0') * 10 + (str[index+1] - '0')) < 27){v2 = run(str,index+2);}return v1 + v2;}
264丑数II
class Solution {public int nthUglyNumber(int n) {int[] factors = {2, 3, 5};Set<Long> seen = new HashSet<Long>();PriorityQueue<Long> heap = new PriorityQueue<Long>();seen.add(1L);heap.offer(1L);int ugly = 0;for (int i = 0; i < n; i++) {long curr = heap.poll();ugly = (int) curr;for (int factor : factors) {long next = curr * factor;if (seen.add(next)) {heap.offer(next);}}}return ugly;}}public int nthUglyNumber(int n) {int[] dp = new int[n + 1];dp[1] = 1;int p2 = 1, p3 = 1, p5 = 1;// 丑数 x 2|3|5 也是丑数,因此算下前面的丑数分别乘以235后的数比对下,取最小值// 对于取到的系数,后面再算丑数的时候基数就要以第二小的丑数为基数了for (int i = 2; i <= n; i++) {int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;dp[i] = Math.min(Math.min(num2, num3), num5);if (dp[i] == num2) {p2++;}if (dp[i] == num3) {p3++;}if (dp[i] == num5) {p5++;}}return dp[n];}
96不同的二叉搜索树
public int numTrees(int n) {int[] dp = new int[n+1];dp[0] = dp[1] = 1;// n = i 时的二叉搜索树数量for (int i = 2; i <= n; i++) {// 在 1...i 的范围,每个j做root时,计算左右子树的数量for (int j = 1; j <= i; j++) {dp[i] += (dp[j-1] * dp[i-j]);}}return dp[n];}// n个节点能构成几个二叉搜索树public int numTrees2(int n) {if (n == 0 || n == 1){return 1;}int count = 0;for (int i = 1; i <= n; i++) {// 以i为root,左边有i-1个节点int left = numTrees(i-1);// 右边有n-i个节点,这里只是计算构成搜索树的数量// 因为右边的树的节点的值是i+1....n 构成的子树数量 == 1...n-i,所以直接按照n-i来算即可int right = numTrees(n-i);// 左右子树数量笛卡尔积作为最终组合树的数量count += (left * right);}return count;}
118杨辉三角
public List<List<Integer>> generate(int numRows) {List<List<Integer>> ans = new ArrayList<>();List<Integer> dp = new ArrayList<>(numRows);for (int i = 1; i <= numRows; i++) {List<Integer> temp = new ArrayList<>();for (int j = 0; j < i; j++) {if (j == 0 || j == i-1){temp.add(1);}else {temp.add(dp.get(j-1)+dp.get(j));}}ans.add(temp);dp = temp;}return ans;}
119杨辉三角II
public List<Integer> getRow(int numRows) {List<Integer> dp = new ArrayList<>(numRows);for (int i = 1; i <= numRows+1; i++) {List<Integer> temp = new ArrayList<>();for (int j = 0; j < i; j++) {if (j == 0 || j == i-1){temp.add(1);}else {temp.add(dp.get(j-1)+dp.get(j));}}dp = temp;if (i == numRows+1){return dp;}}return null;}
70爬楼梯
public int climbStairs(int n) {int[] dp = new int[n+1];dp[n] = 1;for (int i = n-1; i >=0; i--) {int v1 = dp[i+1];int v2 = i+2 > n ? 0 : dp[i+2];dp[i] = v1+v2;}return dp[0];}public int climbStairs2(int n) {return run(n,0);}private int run(int n,int index){if (index == n){return 1;}if (index > n){return 0;}int v1 = run(n,index+1);int v2 = run(n,index+2);return v1+v2;}
322零钱兑换
public int coinChange(int[] coins, int amount) {if (amount <= 0) return 0;int[] dp = new int[amount+1];for (int i = 1; i <= amount; i++) {int count = Integer.MAX_VALUE;for (int coin : coins) {// 注意此时amount取值为iint index = i - coin;if (index >= 0 && dp[index] != Integer.MAX_VALUE){count = Math.min(count,dp[index]+1);}}dp[i] = count;}return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];}public int coinChange2(int[] coins, int amount) {int ans = run(coins,amount);return ans == Integer.MAX_VALUE ? -1 : ans;}//要凑成amount数量需要的最少硬币数private int run(int[] coins, int amount){if (amount < 0){return Integer.MAX_VALUE;}if(amount == 0){return 0;}int count = Integer.MAX_VALUE;for (int coin : coins) {int res = run(coins,amount-coin);if (res != Integer.MAX_VALUE){count = Math.min(count,1+res);}}return count;}
416分割等和子集
public boolean canPartition(int[] nums) {int n = nums.length;if (n < 2) return false;int sum = 0 , max = nums[0];for (int num : nums) {sum += num;max = Math.max(max,num);}if ((sum & 1) == 1){return false;}int half = sum/2;if (max > half){return false;}boolean[][] dp = new boolean[n][half+1];for (int i = 0; i < n; i++) {dp[i][0] = true;}dp[0][nums[0]] = true;for (int i = 1; i < n; i++) {int num = nums[i];for (int j = 0; j <= half; j++) {if (j >= num){// 当前num选择或者不选择dp[i][j] = dp[i-1][j-num] | dp[i-1][j];}else {dp[i][j] = dp[i-1][j];}}}return dp[n-1][half];}public boolean canPartition2(int[] nums) {return run(nums,0,0,0);}// [index...n] 是否满足private boolean run(int[] nums,int index,int lsum,int rsum){if (index == nums.length){return lsum == rsum;}boolean ans = false;ans |= run(nums,index+1,lsum+nums[index],rsum);if (ans) return true;ans |= run(nums,index+1,lsum,rsum+nums[index]);return ans;}
931下降路径最小和
public int minFallingPathSum(int[][] matrix) {int row = matrix.length , col = matrix[0].length;int[][] dp = new int[row][col];for (int i = 0; i < col; i++) {dp[row-1][i] = matrix[row-1][i];}for (int i = row-2; i >= 0; i--) {for (int j = 0; j < col; j++) {int v1 = Integer.MAX_VALUE;if (j > 0){v1 = dp[i+1][j-1];}int v2 = dp[i+1][j];int v3 = Integer.MAX_VALUE;if (j < col-1){v3 = dp[i+1][j+1];}dp[i][j] = matrix[i][j] + Math.min(Math.min(v1,v2),v3);}}return Arrays.stream(dp[0]).min().getAsInt();}public int minFallingPathSum2(int[][] matrix) {int ans = Integer.MAX_VALUE;for (int i = 0; i < matrix.length; i++) {ans = Math.min(ans,run(matrix,0,i));}return ans;}//[row...n]private int run(int[][] matrix, int row, int column){if (row == matrix.length){return 0;}int v1 = Integer.MAX_VALUE;if (column > 0){v1 = run(matrix,row+1,column-1);}int v2 = run(matrix,row+1,column);int v3 = Integer.MAX_VALUE;if (column < matrix[0].length-1){v3 = run(matrix,row+1,column+1);}return matrix[row][column] + Math.min(Math.min(v1,v2),v3);}
120三角形最小路径和
public int minimumTotal(List<List<Integer>> triangle) {int n = triangle.size();//只跟下一行的当前列和下一列有关,当前列的算好后,后面列的不会再用到当前列int[] dp = new int[n];for (int i = 0; i < n; i++) {dp[i] = triangle.get(n-1).get(i);}for (int i = n-2; i >=0 ; i--) {for (int j = 0; j <= i; j++) {int v1 = dp[j+1];int v2 = dp[j];dp[j] = triangle.get(i).get(j) + Math.min(v1,v2);}}return dp[0];}public int minimumTotal1(List<List<Integer>> triangle) {int n = triangle.size();int[][] dp = new int[n][n];for (int i = 0; i < n; i++) {dp[n-1][i] = triangle.get(n-1).get(i);}for (int i = n-2; i >=0 ; i--) {for (int j = 0; j <= i; j++) {int v1 = dp[i+1][j+1];int v2 = dp[i+1][j];dp[i][j] = triangle.get(i).get(j) + Math.min(v1,v2);}}return dp[0][0];}public int minimumTotal2(List<List<Integer>> triangle) {return run(triangle,0,0);}private int run(List<List<Integer>> triangle,int row,int col){if (row == triangle.size()){return 0;}int v1 = run(triangle,row+1,col+1);int v2 = run(triangle,row+1,col);return triangle.get(row).get(col) + Math.min(v1,v2);}
62不同路径
public int uniquePaths(int m, int n) {int[][] dp = new int[m][n];for (int i = 0; i < m; i++) {dp[i][0] = 1;}for (int i = 0; i < n; i++) {dp[0][i] = 1;}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[i][j] = dp[i-1][j] + dp[i][j-1];}}return dp[m-1][n-1];}public int uniquePaths2(int m, int n) {return run(m,n,m-1,n-1);}// 到达tm,tn的不同路径数量private int run(int m, int n,int tm,int tn){if (tm == m || tn == n){return 0;}if (tm == 0 || tn == 0){return 1;}int ans = 0;if (tm < m){// 到达tm-1行的次数ans += run(m,n,tm-1,tn);}if (tn < n){// 到达tn-1列的次数ans += run(m,n,tm,tn-1);}return ans;}
63不同路径II
public int uniquePathsWithObstacles(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;int[][] dp = new int[m][n];for (int i = 0; i < m; i++) {if (obstacleGrid[i][0] == 1){break;}dp[i][0] = 1;}for (int i = 0; i < n; i++) {if (obstacleGrid[0][i] == 1){break;}dp[0][i] = 1;}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i-1][j]+dp[i][j-1];}}return dp[m-1][n-1];}public int uniquePathsWithObstacles2(int[][] obstacleGrid) {int m = obstacleGrid.length;int n = obstacleGrid[0].length;return run(obstacleGrid,m,n,m-1,n-1);}// 到达tm,tnprivate int run(int[][] obstacleGrid,int m,int n,int tm,int tn){if (tm == m || tn == n || obstacleGrid[tm][tn] == 1){return 0;}if (tm == 0 || tn == 0){return 1;}int ans = 0;if (tm < m){ans += run(obstacleGrid,m,n,tm-1,tn);}if (tn < n){ans += run(obstacleGrid,m,n,tm,tn-1);}return ans;}
64最小路径和
public int minPathSum(int[][] grid) {int m = grid.length,n = grid[0].length;int[][]dp = new int[m][n];dp[0][0] = grid[0][0];for (int i = 1; i < m; i++) {dp[i][0] = dp[i-1][0]+grid[i][0];}for (int i = 1; i < n; i++) {dp[0][i] = dp[0][i-1]+grid[0][i];}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {dp[i][j] = grid[i][j] + Math.min(dp[i-1][j],dp[i][j-1]);}}return dp[m-1][n-1];}public int minPathSum2(int[][] grid) {int m = grid.length,n = grid[0].length;return run(grid,m,n,m-1,n-1);}private int run(int[][] grid,int m,int n,int tm,int tn){if (tm < 0 || tn < 0 ){return Integer.MAX_VALUE;}if (tm == 0 && tn == 0){return grid[0][0];}int v1 = run(grid,m,n,tm-1,tn);int v2 = run(grid,m,n,tm,tn-1);int ans = grid[tm][tn];return ans + Math.min(v1,v2);}
221最大正方形
public int maximalSquare(char[][] matrix) {int m = matrix.length,n = matrix[0].length;// i,j处 1组成的正方形的连续长度// 取相邻三个位置的长度最小值+1// 边界处最多为1int[][] dp = new int[m][n];int max = 0;// 边界for (int i = 0; i < m; i++) {if (matrix[i][0] == '1'){dp[i][0] = 1;max = 1;}}for (int i = 0; i < n; i++) {if (matrix[0][i] == '1'){dp[0][i] = 1;max = 1;}}for (int i = 1; i < m; i++) {for (int j = 1; j < n; j++) {if (matrix[i][j] == '1'){dp[i][j] = 1+Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);}max = Math.max(max,dp[i][j]);}}return max*max;}
5最长回文串
516最长回文子序列
public int longestPalindromeSubseq(String s) {int[][] dp = new int[s.length()][s.length()];for (int l = 0; l < dp.length; l++) {for (int r = 0; r < dp.length; r++) {if (l == r){dp[l][r] = 1;}if (l == r-1){dp[l][r] = s.charAt(l) == s.charAt(r) ? 2 : 1;}}}for (int l = s.length()-3; l >=0; l--) {for (int r = 2+l; r <s.length(); r++) {// 一共四种情况// 一定不以l人作为左右两端int v1 = dp[l+1][r-1];// 一定不以l做左端点,但可能以r为右端点int v2 = dp[l+1][r];// 一定不以r做右端点,但可能以l为左端点int v3 = dp[l][r-1];// 一定以lr为端点,判断相等序列长度+2并判断不以lr为端点情况,否则直接0int v4 = s.charAt(l) == s.charAt(r) ? 2 + v1 : 0;dp[l][r] = Math.max(Math.max(v1,v2),Math.max(v3,v4));}}return dp[0][s.length()-1];}// str在[l...r]范围内的最长回文子序列长度private int run(char[] str,int l,int r){if (l == r){return 1;}if (l == r-1){return str[l] == str[r] ? 2 : 1;}// 一共四种情况// 一定不以l人作为左右两端int v1 = run(str,l+1,r-1);// 一定不以l做左端点,但可能以r为右端点int v2 = run(str,l+1,r);// 一定不以r做右端点,但可能以l为左端点int v3 = run(str,l,r-1);// 一定以lr为端点,判断相等序列长度+2并判断不以lr为端点情况,否则直接0int v4 = str[l] == str[r] ? 2 + v1 : 0;return Math.max(Math.max(v1,v2),Math.max(v3,v4));}
300最长递增子序列
public int lengthOfLIS(int[] nums) {int n = nums.length;int[] dp = new int[n];Arrays.fill(dp,1);for (int i = 1; i < n; i++) {for (int j = 0; j < i; j++) {if (nums[i] > nums[j]){dp[i] = Math.max(dp[i],dp[j]+1);}}}return Arrays.stream(dp).max().getAsInt();}public int lengthOfLIS2(int[] nums) {return run(nums,nums.length-1);}// index 处和 0..index-1处的一个个的比较,如果更大就+1,得出index处最大的长度private int run(int[] nums,int index){if (index == 0){return 1;}int cur = nums[index];int ans = 1;for (int i = 0; i < index; i++) {if (cur > nums[i]){int next = run(nums,i);ans = Math.max(ans,next+1);}}return ans;}
376摆动序列
public int wiggleMaxLength(int[] nums) {int n = nums.length;if (n < 2) {return n;}int[] up = new int[n];int[] down = new int[n];up[0] = down[0] = 1;for (int i = 1; i < n; i++) {if (nums[i] > nums[i - 1]) {up[i] = Math.max(up[i - 1], down[i - 1] + 1);down[i] = down[i - 1];} else if (nums[i] < nums[i - 1]) {up[i] = up[i - 1];down[i] = Math.max(up[i - 1] + 1, down[i - 1]);} else {up[i] = up[i - 1];down[i] = down[i - 1];}}return Math.max(up[n - 1], down[n - 1]);}


