class Solution {
public int fib(int n) {
if (n < 2) {
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];
}
}
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
class Solution {
public int climbStairs(int n) {
if (n < 3) {
return n;
}
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
cost
,其中 cost[i]
是从楼梯第 i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length + 1];
for (int i = 2; i <= cost.length; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp[cost.length];
}
}
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?
class Solution {
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];
}
}
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1
和 0
来表示。
class Solution {
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++) {
if (obstacleGrid[i][j] == 1) {
continue;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
}
给定一个正整数 n
,将其拆分为 k
个 正整数 的和( k >= 2
),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n + 1];
dp[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i / 2 + 1; j++) {
//每次计算dp[i],取最大值
dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j)));
}
}
return dp[n];
}
}
n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。class Solution {
public int numTrees(int n) {
//dp[i] 以1 ... i为节点组成二叉树的种数;
//f(j) 以j为节点二叉数的种数;
//dp[i] = f(1) + f(2) + ... +f(i);
//f(j) = dp[j - 1] * dp[i - j];
int[] dp = new int[n + 1];
dp[1] = 1;
dp[0] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
}
class Solution {
public int maxBags(int[] weight, int[] value, int bagSize) {
//dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
int[][] dp = new int[value.length][bagSize + 1];
for (int i = weight[i]; i <= bagSize; i++) {
dp[0][i] = value[0];
}
for (int i = 1; i < value.length; i++) {
for (int j = 1; j <= bagSize; j++) {
if (j >= weight[i]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
} esle {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[value.length - 1][bagSize];
}
public int maxBags(int[] weight, int[] value) {
int[] dp = new int[bagSize + 1];
for (int i = 0; i < value.length; i++) {
for (int j = bagSize; j >= weight[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[bagSize];
}
}
class Solution {
public int maxBags(int[] weight, int[] value, int bagSize) {
int[][] dp = new int[value.length][bagSize + 1];
for (int i = weight[i]; i <= bagSize; i++) {
dp[0][i] = value[0];
}
for (int i = 1; i < value.length; i++) {
for (int j = 1; j <= bagSize; j++) {
if (j >= weight[i]) {
dp[i][j] = Math.max(dp[i -][j], dp[i][j - weight[i]] + value[i]);
} esle {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[value.length - 1][bagSize];
}
public int maxBags(int[] weight, int[] value) {
int[] dp = new int[bagSize + 1];
for (int i = 0; i < value.length; i++) {
for (int j = weight[i]; j <= bagSize; j++) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[bagSize];
}
}
class Solution {
public int maxBags(int[] weight, int[] value, int[] nums, int bagSize) {
int[] dp = new int[bagSize + 1];
for (int i = 0; i < value.length; i++) {
for (int j = weight[i]; j <= bagSize; j++) {
for (int k = 0; k <= nums[k] && j >= k * weight[i]; k++) {
dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
}
}
return dp[bagSize];
}
}
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。class Solution {
public boolean canPartition(int[] nums) {
int sum = Arrays.stream(nums).sum();
if (sum % 2 != 0) {
return false;
}
sum /= 2;
int[] dp = new int[sum + 1];
for (int i = 0; i < nums.length; i++) {
for (int j = sum; j >= nums[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return sum == dp[sum];
}
}
有一堆石头,用整数数组 stones
表示。其中 stones[i]
表示第 i
块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x
和 y
,且 x <= y
。那么粉碎的可能结果如下:如果 x == y
,那么两块石头都会被完全粉碎;如果 x != y
,那么重量为 x
的石头将会完全粉碎,而重量为 y
的石头新重量为 y-x
。最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0
。
class Solution {
public int lastStoneWeightII(int[] stones) {
int sums = Arrays.stream(stones).sum();
int sum = sums / 2;
int[] dp = new int[sum + 1];
for (int i = 0; i < stones.length; i++) {
for (int j = sum; j >= stones[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sums - 2 * dp[sum];
}
}
给你一个非负整数数组 nums
和一个整数 target
。向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式。返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = Arrays.stream(nums).sum();
if (sum < -target || sum < target || (sum + target) % 2 != 0) {
return 0;
}
int size = (sum + target) / 2;
size = size > 0 ? size : -size;
//dp[i]: 从数组中获取和i的方法数。
int[] dp = new int[size + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) {
for (int j = size; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[size];
}
}
给你一个二进制字符串数组 strs
和两个整数 m
和 n
。请你找出并返回 strs
的最大子集的长度,该子集中 最多 有 m
个 0
和 n
个 1
。
strs 数组里的元素就是物品,每个物品都是一个!而m 和 n相当于是一个背包,两个维度的背包。
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String str : strs) {
int zero = 0;
int one = 0;
for (char ch : str.toCharArray()) {
if (ch == '0') {zero++;}
if (ch == '1') {one++;}
}
for (int i = m; i >= zero; i--) {
for (int j = n; j >= one; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zero][j - one] + 1);
}
}
}
return dp[m][n];
}
}
给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。假设每一种面额的硬币有无限个。
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j <= amount; j++) {
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
}
nums
,和一个目标整数 target
。请你从 nums
中找出并返回总和为 target
的元素组合的个数。排列问题class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int j = 0; j <= target; j++) {
for (int num : nums) {
if (j >= num) {
dp[j] += dp[j - num];
}
}
}
return dp[target];
}
}
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?
class Solution {
public int climbStairs(int n) {
if (n < 3) {
return n;
}
int m = 2;
int[] dp = new int[n + 1];
dp[0] = 1;
for (int j = 0; j <= n; j++) {
for (int i = 1; i <= m; i++) {
if (j >= i) {
dp[j] += dp[j - i];
}
}
}
return dp[n];
}
}
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。你可以认为每种硬币的数量是无限的。
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int coin : coins) {
for (int j = coin; j <= amount; j++) {
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
}
n
,返回 和为 n
的完全平方数的最少数量 。dp[j] 可以由dp[j - i * i]推出, dp[j - i * i] + 1 便可以凑成dp[j]。选择最小的dp[j]
class Solution {
public int numSquares(int n) {
if (n < 4) {
return n;
}
int[] dp = new int[n + 1];
Arrays.fill(dp, n + 1);
dp[0] = 0;
for (int i = 1; i <= n / 2; i++) {
for (int j = i * i; j <= n; j++) {
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
}
s
和一个字符串列表 wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出 s
。dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词.
如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
class Solution {
public boolean wordBreak(String s, List wordDict) {
//字典里随便取,拼成放到s的背包里
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int j = 0; j <= s.length(); j++) {
for (String str : wordDict) {
if (j >= str.length() && dp[j - str.length()] && str.equals(s.substring(j - str.length(), j))) {
dp[j] = true;
break;
}
}
}
return dp[s.length()];
}
}
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i]
如果不偷第i房间,那么dp[i] = dp[i - 1]
取最大值
class Solution {
public int rob(int[] nums) {
if (nums.length < 2) {
return nums[0];
}
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < nums.length; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[nums.length - 1];
}
}
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
class Solution {
public int rob(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
if (nums.length == 2) {
return Math.max(nums[0], nums[1]);
}
return Math.max(rob(nums, 0, nums.length - 1), rob(nums, 1, nums.length));
}
public int rob(int[] nums, int start, int end) {
int[] dp = new int[end];
dp[start] = nums[start];
dp[start + 1] = Math.max(nums[start], nums[start + 1]);
for (int i = start + 2; i < end; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[end - 1];
}
}
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root
。除了 root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的 root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
class Solution {
public int rob(TreeNode root) {
int[] res = dfs(root);
return Math.max(res[0], res[1]);
}
public int[] dfs(TreeNode root) {
if (root == null) {
return new int[] { 0, 0 };
}
int[] left = dfs(root.left);
int[] right = dfs(root.right);
int[] dp = new int[2];
dp[1] = left[0] + right[0] + root.val;
dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
return dp;
}
}
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
class Solution {
public int maxProfit(int[] prices) {
// dp[i][0]:第i天不持有的最大利润
// dp[i][1]:第i天持有的最大利润
int[] dp = new int[2];
dp[1] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], dp[1] + prices[i]);
dp[1] = Math.max(dp[1], -prices[i]);
}
return dp[0];
}
}
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。返回 你能获得的 最大 利润 。
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[2];
dp[1] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], dp[1] + prices[i]);
dp[1] = Math.max(dp[1], dp[0] - prices[i]);
}
return dp[0];
}
}
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
class Solution {
public int maxProfit(int[] prices) {
// dp[0]:不持有,dp[1]:第一次持有,dp[2]:第二次不持有,dp[3]:第二次持有,dp[4]:第二次不持有
int[] dp = new int[5];
dp[1] = -prices[0];
dp[3] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[1] = Math.max(dp[1], dp[0] - prices[i]);
dp[2] = Math.max(dp[2], dp[1] + prices[i]);
dp[3] = Math.max(dp[3], dp[2] - prices[i]);
dp[4] = Math.max(dp[4], dp[3] + prices[i]);
}
return Math.max(dp[2], dp[4]);
}
}
给你一个整数数组 prices
和一个整数 k
,其中 prices[i]
是某支给定的股票在第 i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 k
笔交易。也就是说,你最多可以买 k
次,卖 k
次。
class Solution {
public int maxProfit(int k, int[] prices) {
int[] dp = new int[2 * k + 1];
for (int i = 1; i <= 2 * k; i += 2) {
dp[i] = -prices[0];
}
for (int i = 1; i < prices.length; i++) {
for (int j = 1; j <= 2 * k; j += 2) {
dp[j] = Math.max(dp[j], dp[j - 1] - prices[i]);
dp[j + 1] = Math.max(dp[j + 1], dp[j] + prices[i]);
}
}
return dp[2 * k];
}
}
prices
,其中第 prices[i]
表示第 i
天的股票价格 。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。class Solution {
public int maxProfit(int[] prices) {
// dp[i][0]:第i天不持有股票,dp[i][1]:第i天持有股票
if (prices.length < 2) {
return 0;
}
int[][] dp = new int[prices.length][2];
dp[0][1] = -prices[0];
dp[1][0] = Math.max(dp[0][0], dp[0][1] + prices[1]);
dp[1][1] = Math.max(dp[0][1], -prices[1]);
for (int i = 2; 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 - 2][0] - prices[i]);
}
return dp[prices.length - 1][0];
}
}
给定一个整数数组 prices
,其中 prices[i]
表示第 i
天的股票价格 ;整数 fee
代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。返回获得利润的最大值。注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
class Solution {
public int maxProfit(int[] prices, int fee) {
// 买入的时候扣手续费
int[] dp = new int[2];
dp[1] = -prices[0] - fee;
for (int i = 1; i < prices.length; i++) {
dp[1] = Math.max(dp[1], dp[0] - prices[i] - fee);
dp[0] = Math.max(dp[0], dp[1] + prices[i]);
}
return Math.max(dp[1], dp[0]);
}
}
nums
,找到其中最长严格递增子序列的长度。class Solution {
public int lengthOfLIS(int[] nums) {
// 1、dp[i]:以nums[i]为结尾最长递增子序列的长度
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
int res = 1;
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
class Solution {
public int findLengthOfLCIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
int res = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) {
dp[i] = dp[i - 1] + 1;
}
res = Math.max(res, dp[i]);
}
return res;
}
}
nums1
和 nums2
,返回 两个数组中 公共的 、长度最长的子数组的长度 class Solution {
public int findLength(int[] nums1, int[] nums2) {
// dp[i][j] nums1以i-1为结尾,num2以j-1为结尾的公共最长子数组长度
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
int res = 0;
for (int i = 1; i <= nums1.length; i++) {
for (int j = 1; j <= nums2.length; j++) {
if (nums1[i - 1] == nums2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
res = Math.max(res, dp[i][j]);
}
}
return res;
}
}
text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
// dp[i][j]:以text1[i - 1],text2[j - 1]为结尾,最长公共子序列长度
int[][] dp = new int[text1.length() + 1][text2.length() + 1];
for (int i = 1; i <= text1.length(); i++) {
for (int j = 1; j <= text2.length(); j++) {
if (text1.charAt(i - 1) == text2.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]);
}
}
}
return dp[text1.length()][text2.length()];
}
}
class Solution {
public int maxUncrossedLines(int[] nums1, int[] nums2) {
// dp[i][j]:以text1[i - 1],text2[j - 1]为结尾,最长公共子序列长度
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
for (int i = 1; i <= nums1.length; i++) {
for (int j = 1; j <= nums2.length; j++) {
if (nums1[i - 1] == nums2[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]);
}
}
}
return dp[nums1.length][nums2.length];
}
}
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length + 1];
int res = nums[0];
dp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
if (dp[i - 1] > 0) {
dp[i] = dp[i - 1] + nums[i];
} else {
dp[i] = nums[i];
}
res = Math.max(res, dp[i]);
}
return res;
}
}
class Solution {
public boolean isSubsequence(String s, String t) {
// dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
int[][] dp = new int[s.length() + 1][t.length() + 1];
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= t.length(); j++) {
if (s.charAt(i - 1) == t.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = dp[i][j - 1];
}
}
}
return dp[s.length()][t.length()] == s.length();
}
public boolean isSubsequence(String s, String t) {
if (s.length() == 0) {
return true;
}
int index = 0;
for (char ch : t.toCharArray()) {
if (s.charAt(index) == ch) {
index++;
}
if (index == s.length()) {
return true;
}
}
return index == s.length();
}
}
s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。 一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]
一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]
class Solution {
public int numDistinct(String s, String t) {
// dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
int[][] dp = new int[s.length() + 1][t.length() + 1];
for (int i = 0; i <= s.length(); i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= t.length(); j++) {
if (s.charAt(i - 1) == t.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[s.length()][t.length()];
}
}
word1
和 word2
,返回使得 word1
和 word2
相同所需的最小步数。class Solution {
public int minDistance(String word1, String word2) {
int[][] dp = new int[word1.length() + 1][word2.length() + 1];
for (int i = 0; i <= word1.length(); i++) {
dp[i][0] = i;
}
for (int j = 0; j <= word2.length(); j++) {
dp[0][j] = j;
}
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.length(); j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
}
}
}
return dp[word1.length()][word2.length()];
}
}
给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。你可以对一个单词进行如下三种操作:插入一个字符;删除一个字符;替换一个字符
class Solution {
public int minDistance(String word1, String word2) {
int[][] dp = new int[word1.length() + 1][word2.length() + 1];
for (int i = 0; i <= word1.length(); i++) {
dp[i][0] = i;
}
for (int j = 0; j <= word2.length(); j++) {
dp[0][j] = j;
}
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.length(); j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
}
}
return dp[word1.length()][word2.length()];
}
}
给你一个字符串 s
,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。
class Solution {
public int countSubstrings(String s) {
//dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串
boolean[][] dp = new boolean[s.length()][s.length()];
int res = 0;
for (int i = s.length() - 1; i >= 0; i--) {
for (int j = i; j < s.length(); j++) {
if (s.charAt(j) == s.charAt(i) && (j - i <= 1 || dp[i + 1][j - 1])) {
dp[i][j] = true;
res++;
}
}
}
return res;
}
}
s
,找出其中最长的回文子序列,并返回该序列的长度。class Solution {
public int longestPalindromeSubseq(String s) {
// dp[i][j] [i, j]范围内的最长回文子串
int[][] dp = new int[s.length()][s.length()];
for (int i = s.length() - 1; i >= 0; i--) {
for (int j = i; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
if (j - i <= 1) {
dp[i][j] = j - i + 1;
} else {
dp[i][j] = dp[i + 1][j - 1] + 2;
}
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][s.length() - 1];
}
}