1【简单】剑指 Offer 10- I. 斐波那契数列

/** 动态规划,2开始,取模1e9+7*/public int fib(int n) {final int MOD = 1000000007;if (n < 2) {return n;}int p = 0, q = 0, r = 1;for (int i = 2; i <= n; ++i) {p = q;q = r;r = (p + q) % MOD;}return r;}
2【困难】最长有效括号(32)


/** 分两种情况,一种前一个元素为(,一种前一种元素为)*/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;}
3【中等】跳跃游戏(55)

/** 每个位置都有个最远处的映射,只要当前最远处能映射到终点就可以了*/public boolean canJump(int[] nums) {// 能跑到最远处下标int maxPos = nums[0];for (int i = 1; i < nums.length; ++i) {if (i > maxPos) {// 跑不到return false;}// 不断求得最远处的下标maxPos = Math.max(i + nums[i], maxPos);}return true;}
4【中等】跳跃游戏 II(45)

动态规划
/** 动态规划,dp[i]=Math.min(dp[i],dp[j]+1)*/public int jump(int[] nums) {long[] dp = new long[nums.length];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for (int i = 1; i < nums.length; i++) {// j代表到达i的前一步,所以是+1for (int j = 0; j < nums.length; j++) {if (nums[j] + j >= i) {dp[i] = Math.min(dp[i], dp[j] + 1);}}}return dp[nums.length - 1] == Integer.MAX_VALUE ? 0 : (int) dp[nums.length - 1];}
贪心
/** 假设每次都能到达最远位置*/public int jump(int[] nums) {int length = nums.length;int end = 0;int maxPosition = 0;int steps = 0;for (int i = 0; i < length - 1; i++) {maxPosition = Math.max(maxPosition, i + nums[i]);if (i == end) {end = maxPosition;steps++;}}return steps;}
5【简单】最大子数组和(53)

/** f(i)=Max(f(i-1)+nums[i],nums[i])*/public int maxSubArray(int[] nums) {int pre = 0, maxAns = nums[0];for (int x : nums) {pre = Math.max(pre + x, x);maxAns = Math.max(maxAns, pre);}return maxAns;}
6【中等】不同路径(62)

/** f(i,j)=f(i-1,j)+f(i,j-1)*/public int uniquePaths(int m, int n) {int[][] f = new int[m][n];for (int i = 0; i < m; ++i) {f[i][0] = 1;}for (int j = 0; j < n; ++j) {f[0][j] = 1;}for (int i = 1; i < m; ++i) {for (int j = 1; j < n; ++j) {f[i][j] = f[i - 1][j] + f[i][j - 1];}}return f[m - 1][n - 1];}
7【中等】不同路径2(63)

/** f(i,j)=f(i-1,j)+f(i,j-1)||0* 本题压缩为1维数组,难以理解,用上提的二维数组一样可以解决*/public int uniquePathsWithObstacles(int[][] obstacleGrid) {int n = obstacleGrid.length, m = obstacleGrid[0].length;int[] f = new int[m];f[0] = obstacleGrid[0][0] == 0 ? 1 : 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (obstacleGrid[i][j] == 1) {f[j] = 0;continue;}if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {f[j] += f[j - 1];}}}return f[m - 1];}
8【中等】最小路径和(64)

/** f(i,j)=Min(f(i-1,j)+f(i,j-1))+grid[i][j]*/public int minPathSum(int[][] grid) {if (grid == null || grid.length == 0 || grid[0].length == 0) {return 0;}int rows = grid.length, columns = grid[0].length;int[][] dp = new int[rows][columns];dp[0][0] = grid[0][0];for (int i = 1; i < rows; i++) {dp[i][0] = dp[i - 1][0] + grid[i][0];}for (int j = 1; j < columns; j++) {dp[0][j] = dp[0][j - 1] + grid[0][j];}for (int i = 1; i < rows; i++) {for (int j = 1; j < columns; j++) {dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];}}return dp[rows - 1][columns - 1];}
9【中等】一和零(474)


public class FindMaxForm {public int findMaxForm(String[] strs, int m, int n) {int[][] dp = new int[m + 1][n + 1];int length = strs.length;for (int i = 0; i < length; i++) {int[] zerosOnes = getZerosOnes(strs[i]);int zeros = zerosOnes[0], ones = zerosOnes[1];for (int j = m; j >= zeros; j--) {for (int k = n; k >= ones; k--) {dp[j][k] = Math.max(dp[j][k], dp[j - zeros][k - ones] + 1);}}}return dp[m][n];}public int[] getZerosOnes(String str) {int[] zerosOnes = new int[2];int length = str.length();for (int i = 0; i < length; i++) {zerosOnes[str.charAt(i) - '0']++;}return zerosOnes;}}
10【中等】目标和(494)


public int findTargetSumWays(int[] nums, int target) {int sum = 0;for (int num : nums) {sum += num;}int diff = sum - target;if (diff < 0 || diff % 2 != 0) {return 0;}int neg = diff / 2;int[] dp = new int[neg + 1];dp[0] = 1;for (int num : nums) {for (int j = neg; j >= num; j--) {dp[j] += dp[j - num];}}return dp[neg];}
11【中等】零钱兑换(322)


public int coinChange(int[] coins, int amount) {int max = amount + 1;int[] dp = new int[amount + 1];Arrays.fill(dp, max);dp[0] = 0;for (int i = 1; i <= amount; i++) {for (int j = 0; j < coins.length; j++) {if (coins[j] <= i) {dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);}}}return dp[amount] > amount ? -1 : dp[amount];}
12【中等】零钱兑换 II(518)

public static int change(int amount, int[] coins) {int[] dp = new int[amount + 1];dp[0] = 1;for (int coin : coins) {for (int i = coin; i <= amount; i++) {dp[i] += dp[i - coin];}}return dp[amount];}
13【中等】最长回文子串(5)

/** 先枚举子串长度,再从左到右遍历*/public String longestPalindrome(String s) {int len = s.length();if (len < 2) {return s;}int maxLen = 1;int begin = 0;// dp[i][j] 表示 s[i..j] 是否是回文串boolean[][] dp = new boolean[len][len];// 初始化:所有长度为 1 的子串都是回文串for (int i = 0; i < len; i++) {dp[i][i] = true;}char[] charArray = s.toCharArray();// 递推开始// 先枚举子串长度for (int L = 2; L <= len; L++) {// 枚举左边界,左边界的上限设置可以宽松一些for (int i = 0; i < len; i++) {// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得int j = L + i - 1;// 如果右边界越界,就可以退出当前循环if (j >= len) {break;}if (charArray[i] != charArray[j]) {dp[i][j] = false;} else {if (j - i < 3) {dp[i][j] = true;} else {dp[i][j] = dp[i + 1][j - 1];}}// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置if (dp[i][j] && j - i + 1 > maxLen) {maxLen = j - i + 1;begin = i;}}}return s.substring(begin, begin + maxLen);}
14【中等】买卖股票的最佳时机 II(122)

定义状态dp[i][0] 表示第 i 天交易完后手里没有股票的最大利润,dp[i][1] 表示第 i 天交易完后手里持有一支股票的最大利润(i 从 0 开始)
class Solution {public int maxProfit(int[] prices) {int n = prices.length;int[][] dp = new int[n][2];dp[0][0] = 0;dp[0][1] = -prices[0];for (int i = 1; i < n; ++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[n - 1][0];}}
15【中等】打家劫舍 II(213)

class Solution {public int rob(int[] nums) {int length = nums.length;if (length == 1) {return nums[0];} else if (length == 2) {return Math.max(nums[0], nums[1]);}return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));}public int robRange(int[] nums, int start, int end) {int first = nums[start], second = Math.max(nums[start], nums[start + 1]);for (int i = start + 2; i <= end; i++) {int temp = second;second = Math.max(first + nums[i], second);first = temp;}return second;}}
