🌟欢迎来到 我的博客 —— 探索技术的无限可能!
🌟博客的简介(文章目录)
【题解】—— 每日一道题目栏
上接:【题解】—— LeetCode一周小结24
17.最长特殊序列 II
题目链接:522. 最长特殊序列 II
给定字符串列表 strs ,返回其中 最长的特殊序列 的长度。如果最长特殊序列不存在,返回 -1 。
特殊序列 定义如下:该序列为某字符串 独有的子序列(即不能是其他字符串的子序列)。
s 的 子序列可以通过删去字符串 s 中的某些字符实现。
例如,“abc” 是 “aebdc” 的子序列,因为您可以删除"aebdc"中的下划线字符来得到 “abc” 。“aebdc"的子序列还包括"aebdc”、 “aeb” 和 “” (空字符串)。
示例 1:
输入: strs = [“aba”,“cdc”,“eae”]
输出: 3
示例 2:
输入: strs = [“aaa”,“aaa”,“aa”]
输出: -1
提示:
2 <= strs.length <= 50
1 <= strs[i].length <= 10
strs[i] 只包含小写英文字母
题解:
方法:枚举
class Solution {
public int findLUSlength(String[] strs) {
int ans = -1;
next:
for (int i = 0; i < strs.length; i++) {
if (strs[i].length() <= ans) { // 不会让 ans 变大
continue;
}
for (int j = 0; j < strs.length; j++) {
if (j != i && isSubseq(strs[i], strs[j])) {
continue next;
}
}
ans = strs[i].length();
}
return ans;
}
// 判断 s 是否为 t 的子序列
private boolean isSubseq(String s, String t) {
int i = 0;
for (char c : t.toCharArray()) {
if (s.charAt(i) == c && ++i == s.length()) { // 所有字符匹配完毕
return true; // s 是 t 的子序列
}
}
return false;
}
}
18.价格减免
题目链接:2288. 价格减免
句子 是由若干个单词组成的字符串,单词之间用单个空格分隔,其中每个单词可以包含数字、小写字母、和美元符号 ‘$’ 。如果单词的形式为美元符号后跟着一个非负实数,那么这个单词就表示一个 价格 。
例如 “$100”、“$23” 和 “
6
"
表示价格,而
"
100
"
、
"
6" 表示价格,而 "100"、"
6"表示价格,而"100"、"” 和 "$1e5 不是。
给你一个字符串 sentence 表示一个句子和一个整数 discount 。对于每个表示价格的单词,都在价格的基础上减免 discount% ,并 更新 该单词到句子中。所有更新后的价格应该表示为一个 恰好保留小数点后两位 的数字。
返回表示修改后句子的字符串。
注意:所有价格 最多 为 10 位数字。
示例 1:
输入:sentence = “there are $1 2 a n d 5 2 and 5 2and5 candies in the shop”, discount =
50输出:“there are $0.50 1.00 a n d 5 1.00 and 5 1.00and5 candies in the shop”
解释:
表示价格的单词是 “$1” 和 “$2” 。
- “$1” 减免 50% 为 “$0.50” ,所以 “$1” 替换为 “$0.50” 。
- “$2” 减免 50% 为 “$1” ,所以 “$2” 替换为 “$1.00” 。
示例 2:
输入:sentence = “1 2 $3 4 $5 678 6 7 8 678 $9 10 10 10”, discount = 100
输出:“1 2 $0.00 4 $0.00 0.0078 0.00 7 8 0.0078 $0.00 10 10 10”
解释:
任何价格减免 100% 都会得到 0 。
表示价格的单词分别是 “$3”、“$5”、“$6” 和 “$9”。
每个单词都替换为 “$0.00”。
提示:
1 <= sentence.length <= 105
sentence 由小写英文字母、数字、’ ’ 和 ‘$’ 组成
sentence 不含前导和尾随空格
sentence 的所有单词都用单个空格分隔
所有价格都是 正 整数且不含前导零
所有价格 最多 为 10 位数字
0 <= discount <= 100
题解:
方法:模拟
class Solution {
public String discountPrices(String sentence, int discount) {
String[] words = sentence.split(" ");
for (int i = 0; i < words.length; ++i) {
if (check(words[i])) {
double t = Long.parseLong(words[i].substring(1)) * (1 - discount / 100.0);
words[i] = String.format("$%.2f", t);
}
}
return String.join(" ", words);
}
private boolean check(String s) {
if (s.charAt(0) != '$' || s.length() == 1) {
return false;
}
for (int i = 1; i < s.length(); ++i) {
if (!Character.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
}
19.矩阵中严格递增的单元格数
题目链接:2713. 矩阵中严格递增的单元格数
给你一个下标从 1 开始、大小为 m x n 的整数矩阵 mat,你可以选择任一单元格作为 起始单元格 。
从起始单元格出发,你可以移动到 同一行或同一列 中的任何其他单元格,但前提是目标单元格的值 严格大于 当前单元格的值。
你可以多次重复这一过程,从一个单元格移动到另一个单元格,直到无法再进行任何移动。
请你找出从某个单元开始访问矩阵所能访问的 单元格的最大数量 。
返回一个表示可访问单元格最大数量的整数。
示例 1:
输入:mat = [[3,1],[3,4]]
输出:2
解释:上图展示了从第 1 行、第 2 列的单元格开始,可以访问 2 个单元格。可以证明,无论从哪个单元格开始,最多只能访问 2
个单元格,因此答案是 2 。
示例 2:
输入:mat = [[1,1],[1,1]]
输出:1
解释:由于目标单元格必须严格大于当前单元格,在本示例中只能访问 1 个单元格。
示例 3:
输入:mat = [[3,1,6],[-9,5,7]]
输出:4
解释:上图展示了从第 2 行、第 1 列的单元格开始,可以访问 4 个单元格。可以证明,无论从哪个单元格开始,最多只能访问 4
个单元格,因此答案是 4 。
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 105
1 <= m * n <= 105
-105 <= mat[i][j] <= 105
题解:
方法:动态规划
class Solution {
public int maxIncreasingCells(int[][] mat) {
int m = mat.length, n = mat[0].length;
TreeMap<Integer, List<int[]>> g = new TreeMap<>();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
g.computeIfAbsent(mat[i][j], k -> new ArrayList<>()).add(new int[] {i, j});
}
}
int[] rowMax = new int[m];
int[] colMax = new int[n];
int ans = 0;
for (var e : g.entrySet()) {
var pos = e.getValue();
int[] mx = new int[pos.size()];
int k = 0;
for (var p : pos) {
int i = p[0], j = p[1];
mx[k] = Math.max(rowMax[i], colMax[j]) + 1;
ans = Math.max(ans, mx[k++]);
}
for (k = 0; k < mx.length; ++k) {
int i = pos.get(k)[0], j = pos.get(k)[1];
rowMax[i] = Math.max(rowMax[i], mx[k]);
colMax[j] = Math.max(colMax[j], mx[k]);
}
}
return ans;
}
}
20.美丽下标对的数目
题目链接:2748. 美丽下标对的数目
给你一个下标从 0 开始的整数数组 nums 。如果下标对 i、j 满足 0 ≤ i < j < nums.length ,如果 nums[i] 的 第一个数字 和 nums[j] 的 最后一个数字 互质 ,则认为 nums[i] 和 nums[j] 是一组 美丽下标对 。
返回 nums 中 美丽下标对 的总数目。
对于两个整数 x 和 y ,如果不存在大于 1 的整数可以整除它们,则认为 x 和 y 互质 。换而言之,如果 gcd(x, y) == 1 ,则认为 x 和 y 互质,其中 gcd(x, y) 是 x 和 y 的 最大公因数 。
示例 1:
输入:nums = [2,5,1,4]
输出:5
解释:nums 中共有 5 组美丽下标对:
i = 0 和 j = 1 :nums[0] 的第一个数字是 2 ,nums[1] 的最后一个数字是 5 。2 和 5 互质,因此
gcd(2,5) == 1 。i = 0 和 j = 2 :nums[0] 的第一个数字是 2 ,nums[2] 的最后一个数字是 1 。2 和 1 互质,因此
gcd(2,1) == 1 。i = 1 和 j = 2 :nums[1] 的第一个数字是 5 ,nums[2] 的最后一个数字是 1 。5 和 1 互质,因此
gcd(5,1) == 1 。i = 1 和 j = 3 :nums[1] 的第一个数字是 5 ,nums[3] 的最后一个数字是 4 。5 和 4 互质,因此
gcd(5,4) == 1 。i = 2 和 j = 3 :nums[2] 的第一个数字是 1 ,nums[3] 的最后一个数字是 4 。1 和 4 互质,因此
gcd(1,4) == 1 。因此,返回 5 。
示例 2:
输入:nums = [11,21,12]
输出:2
解释:共有 2 组美丽下标对:
i = 0 和 j = 1 :nums[0] 的第一个数字是 1 ,nums[1] 的最后一个数字是 1 。gcd(1,1) == 1 。
i = 0 和 j = 2 :nums[0] 的第一个数字是 1 ,nums[2] 的最后一个数字是 2 。gcd(1,2) == 1 。
因此,返回 2 。
提示:
2 <= nums.length <= 100
1 <= nums[i] <= 9999
nums[i] % 10 != 0
题解:
方法:数论
class Solution {
public int countBeautifulPairs(int[] nums) {
int ans = 0;
int[] cnt = new int[10];
for (int x : nums) {
for (int y = 1; y < 10; y++) {
if (cnt[y] > 0 && gcd(y, x % 10) == 1) {
ans += cnt[y];
}
}
while (x >= 10) {
x /= 10;
}
cnt[x]++; // 统计最高位的出现次数
}
return ans;
}
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
21.气温变化趋势
题目链接:LCP 61. 气温变化趋势
力扣城计划在两地设立「力扣嘉年华」的分会场,气象小组正在分析两地区的气温变化趋势,对于第 i ~ (i+1) 天的气温变化趋势,将根据以下规则判断:
若第 i+1 天的气温 高于 第 i 天,为 上升 趋势
若第 i+1 天的气温 等于 第 i 天,为 平稳 趋势
若第 i+1 天的气温 低于 第 i 天,为 下降 趋势
已知 temperatureA[i] 和 temperatureB[i] 分别表示第 i 天两地区的气温。 组委会希望找到一段天数尽可能多,且两地气温变化趋势相同的时间举办嘉年华活动。请分析并返回两地气温变化趋势相同的最大连续天数。
即最大的 n,使得第 i~i+n 天之间,两地气温变化趋势相同
示例 1:
输入: temperatureA = [21,18,18,18,31] temperatureB = [34,32,16,16,17]
输出:2
解释:如下表所示, 第 2~4 天两地气温变化趋势相同,且持续时间最长,因此返回 4-2=2
示例 2:
输入: temperatureA = [5,10,16,-6,15,11,3] temperatureB =
[16,22,23,23,25,3,-16]输出:3
提示:
2 <= temperatureA.length == temperatureB.length <= 1000
-20 <= temperatureA[i], temperatureB[i] <= 40
题解:
方法:数学
public class Solution {
public int temperatureTrend(int[] a, int[] b) {
int ans = 0;
int same = 0;
for (int i = 1; i < a.length; i++) {
if (Integer.compare(a[i - 1], a[i]) == Integer.compare(b[i - 1], b[i])) {
ans = Math.max(ans, ++same);
} else {
same = 0;
}
}
return ans;
}
}
22.字典序最小的美丽字符串
题目链接:2663. 字典序最小的美丽字符串
如果一个字符串满足以下条件,则称其为 美丽字符串 :
它由英语小写字母表的前 k 个字母组成。
它不包含任何长度为 2 或更长的回文子字符串。
给你一个长度为 n 的美丽字符串 s 和一个正整数 k 。
请你找出并返回一个长度为 n 的美丽字符串,该字符串还满足:在字典序大于 s 的所有美丽字符串中字典序最小。如果不存在这样的字符串,则返回一个空字符串。
对于长度相同的两个字符串 a 和 b ,如果字符串 a 在与字符串 b 不同的第一个位置上的字符字典序更大,则字符串 a 的字典序大于字符串 b 。
例如,“abcd” 的字典序比 “abcc” 更大,因为在不同的第一个位置(第四个字符)上 d 的字典序大于 c 。
示例 1:
输入:s = “abcz”, k = 26
输出:“abda”
解释:字符串 “abda” 既是美丽字符串,又满足字典序大于 “abcz” 。 可以证明不存在字符串同时满足字典序大于
“abcz”、美丽字符串、字典序小于 “abda” 这三个条件。
示例 2:
输入:s = “dc”, k = 4
输出:“”
解释:可以证明,不存在既是美丽字符串,又字典序大于 “dc” 的字符串。
提示:
1 <= n == s.length <= 105
4 <= k <= 26
s 是一个美丽字符串
题解:
方法:贪心
class Solution {
public String smallestBeautifulString(String S, int k) {
k += 'a';
char[] s = S.toCharArray();
int n = s.length;
int i = n - 1; // 从最后一个字母开始
s[i]++; // 先加一
while (i < n) {
if (s[i] == k) { // 需要进位
if (i == 0) { // 无法进位
return "";
}
// 进位
s[i] = 'a';
s[--i]++;
} else if (i > 0 && s[i] == s[i - 1] || i > 1 && s[i] == s[i - 2]) {
s[i]++; // 如果 s[i] 和左侧的字符形成回文串,就继续增加 s[i]
} else {
i++; // 反过来检查后面是否有回文串
}
}
return new String(s);
}
}
23.检测大写字母
题目链接:520. 检测大写字母
我们定义,在以下情况时,单词的大写用法是正确的:
全部字母都是大写,比如 “USA” 。
单词中所有字母都不是大写,比如 “leetcode” 。
如果单词不只含有一个字母,只有首字母大写, 比如 “Google” 。
给你一个字符串 word 。如果大写用法正确,返回 true ;否则,返回 false 。
示例 1:
输入:word = “USA”
输出:true
示例 2:
输入:word = “FlaG”
输出:false
提示:
1 <= word.length <= 100
word 由小写和大写英文字母组成
题解:
方法:
class Solution {
public boolean detectCapitalUse(String word) {
int cnt = 0;
for (char c : word.toCharArray()) {
if (Character.isUpperCase(c)) {
++cnt;
}
}
return cnt == 0 || cnt == word.length()
|| (cnt == 1 && Character.isUpperCase(word.charAt(0)));
}
}
下接:【题解】—— LeetCode一周小结26