两个字符串的最小ASCII删除和
给定两个字符串s1
和 s2
,返回 使两个字符串相等所需删除字符的 ASCII 值的最小和 。
示例 1:
输入: s1 = "sea", s2 = "eat" 输出: 231 解释: 在 "sea" 中删除 "s" 并将 "s" 的值(115)加入总和。 在 "eat" 中删除 "t" 并将 116 加入总和。 结束时,两个字符串相等,115 + 116 = 231 就是符合条件的最小和。
示例 2:
输入: s1 = "delete", s2 = "leet" 输出: 403 解释: 在 "delete" 中删除 "dee" 字符串变成 "let", 将 100[d]+101[e]+101[e] 加入总和。在 "leet" 中删除 "e" 将 101[e] 加入总和。 结束时,两个字符串都等于 "let",结果即为 100+101+101+101 = 403 。 如果改为将两个字符串转换为 "lee" 或 "eet",我们会得到 433 或 417 的结果,比答案更大。
解题:这个题目类似昨天哪个题目,变动的是改动数量,这里的话如果就是将变动数量改为变动字符的ASCII码就行了
class Solution {
public int minimumDeleteSum(String s1, String s2) {
int n = s1.length();
int m = s2.length();
int[][] dp = new int[n + 1][m + 1];
// 初始化第一行和第一列
for (int i = 1; i <= n; i++) {
dp[i][0] += dp[i - 1][0] + s1.charAt(i - 1);
}
for (int j = 1; j <= m; j++) {
dp[0][j] += dp[0][j - 1] + s2.charAt(j - 1);
}
// 动态规划填充表
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1));
}
}
}
// 返回最后一格的值,即最小删除和
return dp[n][m];
}
}
不同子序列
给你两个字符串 s
和 t
,统计并返回在 s
的 子序列 中 t
出现的个数,结果需要对 109 + 7 取模。
示例 1:
输入:s = "rabbbit", t = "rabbit"输出
:3
解释: 如下所示, 有 3 种可以从 s 中得到"rabbit" 的方案
。rabbbit
rabbbit
rabbbit
示例 2:
输入:s = "babgbag", t = "bag"输出
:5
解释: 如下所示, 有 5 种可以从 s 中得到"bag" 的方案
。babgbag
babgbag
babgbag
babgbag
babgbag
class Solution {
public int numDistinct(String s, String t) {
int m = s.length();
int n = t.length();
int[][] dp = new int[n + 1][m + 1];
for (int j = 0; j <= m; j++) {
dp[0][j] = 1; // 初始化第一行为1,表示空字符串是任何字符串的子序列
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (t.charAt(i - 1) == s.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
} else {
dp[i][j] = dp[i][j - 1];
}
}
}
return dp[n][m];
}
}
最长递增子序列
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的
子序列
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3] 输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7] 输出:1
class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 1)
return 1;
int[] dp = new int[nums.length];
int res = 0;
Arrays.fill(dp, 1);
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
//j 到 i 之间最长递增子序列大小
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
最长递增子序列的个数
给定一个未排序的整数数组 nums
, 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。
示例 1:
输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输入: [2,2,2,2,2] 输出: 5 解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
class Solution {
public int findNumberOfLIS(int[] nums) {
int n = nums.length;
int[] lengths = new int[n];
int[] counts = new int[n];
Arrays.fill(lengths, 1);
Arrays.fill(counts, 1);
int maxLength = 0;
int totalMaxCount = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
//第一次找到
if (lengths[j] + 1 > lengths[i]) {
lengths[i] = lengths[j] + 1;
counts[i] = counts[j];
} else if (lengths[j] + 1 == lengths[i]) {
//第n次找到
counts[i] += counts[j];
}
}
}
if (lengths[i] > maxLength) {
maxLength = lengths[i];
totalMaxCount = counts[i];
} else if (lengths[i] == maxLength) {
//如果一次循环后,还是一样的长度,则加上此次遍历的相同长度的数量
totalMaxCount += counts[i];
}
}
return totalMaxCount;
}
}
每日一题:让所有学生保持开心的分组方法数
给你一个下标从 0 开始、长度为 n
的整数数组 nums
,其中 n
是班级中学生的总数。班主任希望能够在让所有学生保持开心的情况下选出一组学生:
如果能够满足下述两个条件之一,则认为第 i
位学生将会保持开心:
- 这位学生被选中,并且被选中的学生人数 严格大于
nums[i]
。 - 这位学生没有被选中,并且被选中的学生人数 严格小于
nums[i]
。
返回能够满足让所有学生保持开心的分组方法的数目。
示例 1:
输入:nums = [1,1] 输出:2 解释: 有两种可行的方法: 班主任没有选中学生。 班主任选中所有学生形成一组。 如果班主任仅选中一个学生来完成分组,那么两个学生都无法保持开心。因此,仅存在两种可行的方法。
示例 2:
输入:nums = [6,0,3,3,6,7,2,7] 输出:3 解释: 存在三种可行的方法: 班主任选中下标为 1 的学生形成一组。 班主任选中下标为 1、2、3、6 的学生形成一组。 班主任选中所有学生形成一组。
class Solution {
public int countWays(List<Integer> nums) {
int n = nums.size(); // 获取列表的大小
int[] a = nums.stream().mapToInt(i -> i).toArray(); // 将列表转换为整型数组
Arrays.sort(a); // 排序数组,以便后续操作
int ans = a[0] > 0 ? 1 : 0; // 如果数组的第一个元素大于0,则至少可以在数组开头插入0
// 遍历排序后的数组
for (int i = 1; i < n; i++) {
// 如果当前位置i可以插入一个数字x(x范围为i),使得a[i - 1] < x < a[i]
if (a[i - 1] < i && i < a[i])
ans++; // 插入方式增加一种
}
// 最后加上一种情况,即在数组末尾插入一个数字n,使得n大于数组最后一个元素
return ans + 1;
}
}