兼具大小写的最好英文字母(3分)
题目链接:力扣:5242.兼具大小写的最好英文字母
解题思路1: 统计字符串中出现的每一个字符(利用桶排序的思想),然后按照26个英文字母表从后往前的顺序判断其(大小写)是否都在数组中出现,若都出现直接返回该字母的大写(以字符串的形式),若26个英文字母都不符合条件,那么返回空字符串。
代码如下:
class Solution {
public String greatestLetter(String s) {
int arr[] = new int['z' + 1];
for(int i = 0; i < s.length(); i++){
arr[s.charAt(i)] = 1;
}
for(int i = 'Z'; i >= 'A'; i--){
if(arr[i] == 1 && arr[i + 32] == 1)
return (char)i + "";
}
return "";
}
}
解题思路2: 上面的方法我们使用一个数组来记录出现的字符,空间复杂度为O(n)。这里讲解一种位运算的方法,将空间复杂度降为O(1)。 下面字母的ASCII码为 A:65 Z:90 a:97 z:122 ,122-65+1=58 因此可以用一个long类型的整形mask来记录有没有出现过某个字母。 代码如下:
class Solution {
public String greatestLetter(String s) {
long mask = 0;
int l = s.length();
for(int i = 0; i < l; i++){
mask |= 1L << (s.charAt(i) - 'A');
}
for(int i = 25; i >= 0; i--){
if((mask >> i & 1) == 1 && (mask >> (i + 32) & 1) == 1){
return (char)(i+65) + "";
}
}
return "";
}
}
个位数字为K的整数之和(4分)
题目链接:力扣:5218.个位数字为K的整数之和
解题思路:
- 当
num == 0 时,很显然返回0。 - 当
k > num 时,因为多重集中的数个位数只能是k,只看个位数都不满足条件,所以这样的多重集一定不存在。 - 进行完以上两种特殊情况判断之后,就来到这道题的核心部分。假设存在这样的多重集使得多重集中的整数之和等于num,我们可以将num看成 :
10*x + y(y是10以内的数字) ,这里的x我们可以不用管(题目只要求多重集中的数个位数必须为k,其它位数都不加限制,10*x 加在多重集中任何一个数上都可以),因此这道题的条件就可以转换为:多重集中整数之和的个位数必须是y(num的个位数) 。又因为只有多重集中每一个整数的个位数(只能是k)影响整数之和的个位数,这道题的条件可以再次转换成:(多重集元素个数*k)%10 == num % 10
代码如下:
class Solution {
public int minimumNumbers(int num, int k) {
if(num == 0) return 0;
if(num < k) return -1;
for(int i = 1; i <= 10; i++){
if(num % 10 == (k*i)%10 && k*i <= num)
return i;
}
return -1;
}
}
小于K的最长二进制子序列(5分)
题目链接:力扣:6099.小于K的最长二进制子序列
解题思路:
-
将所有的0选上一定符合题意的,并且选0一定优于选1。 原因:将一个二进制数中任意一个0换成1只会让该二进制数变得更大。 -
我们可以将所有0选上之后利用贪心,将1插入,插入时应该从后往前插入,因为前面的1对二进制数大小的影响大于后面的数。
例:s = “1001010”, k = 5
- 将0全部选上,“0000”。
- 从后往前将1插入:
(1)“00010”,此时该二进制数的值为2 ,仍然小于5,需要继续插入。 (2)“001010”,此时该二进制数的值为10,大于5,不符合题意。到这里就不在需要插入了,因为插入前面的1只会让该二进制数变得更大。
代码如下:
class Solution {
public int longestSubsequence(String s, int k) {
int l = s.length();
int rnt = 0,tmp = 0,flag = 0;
long sum = 0;
char cur;
for(int i = l - 1; i >=0; i--){
cur = s.charAt(i);
if(cur == '0'){
rnt++;
tmp++;
}
else if(flag == 0){
if(sum + (1L<<tmp) > k)
flag = 1;
else{
sum += (1L<<tmp);
rnt++;
tmp++;
}
}
}
return rnt;
}
}
卖木头块(6分)
题目链接:力扣:5254卖木头块
解题思路: 该题可以使用线性dp 的方法。首先我们定义一个数组arr,arr[ i ][ j ]就是高为i宽为j的木块可以卖的价格。一个木块儿切割之后一定是会变小的,那么我们定义一个dp数组,dp[ i ][ j ]就是高为i,宽为j的木块儿最高能卖的价格,我们可以得到以下关系:
dp[i][j] = arr[i][j];
for(int k = 1; k < i; k++) dp[i][j] = Math.max(dp[i][j],dp[i-k][j] + dp[k][j]);
for(int k = 1; k < j; k++) dp[i][j] = Math.max(dp[i][j],dp[i][j-k] + dp[i][k]);
此时的dp[ i ][ j ]一定是可以卖到的最高价格。 代码如下:
class Solution {
public long sellingWood(int m, int n, int[][] prices) {
int [][]arr = new int[m+1][n+1];
for (int p[] : prices) arr[p[0]][p[1]] = p[2];
long dp[][] = new long[m + 1][n + 1];
for(int i = 1; i <= m ; i++){
for(int j = 1; j <= n; j++){
dp[i][j] = arr[i][j];
for(int k = 1; k < i; k++) dp[i][j] = Math.max(dp[i][j],dp[i-k][j] + dp[k][j]);
for(int k = 1; k < j; k++) dp[i][j] = Math.max(dp[i][j],dp[i][j-k] + dp[i][k]);
}
}
return dp[m][n];
}
}
优化
- 在切割木块时,由于对称性,我们可以减少一半循环。
- 因为我们是从小到大的计算数组dp的值,我们可以不用定义dp数组,直接使用arr数组就行。
优化后代码如下:
class Solution {
public long sellingWood(int m, int n, int[][] prices) {
long [][]arr = new long[m+1][n+1];
for (int p[] : prices) arr[p[0]][p[1]] = p[2];
for(int i = 1; i <= m ; i++){
for(int j = 1; j <= n; j++){
for(int k = 1; k <= i/2; k++) arr[i][j] = Math.max(arr[i][j],arr[i-k][j] + arr[k][j]);
for(int k = 1; k <= j/2; k++) arr[i][j] = Math.max(arr[i][j],arr[i][j-k] + arr[i][k]);
}
}
return arr[m][n];
}
}
|