牛客题库50题解(更新中)
牛客题库50题解
1.小苯的数字权值
题目:
- 小苯定义一个数字的权值为:该数字的正因子个数。
- 小苯现在拿到了一个正整数 ,他希望将 : 分解为若干不等于1的数字(也可以不做分解),使得所有分解出的正整数乘积等于,且所有数字的权值之和尽可能大,你能帮帮他求出最大的权值和吗。
解题思路:
通过率:
代码:
5.小美的数组删除
题目:
- 小美有一个长度为 n 的数组 a1,a2,…,an,他可以对数组进行如下操作:删除第一个元素 a1,同时数组的长度减一,花费为 x。删除整个数组,花费为 k * MEX(a)(其中 MEX(a)表示 a中未出现过的最小非负整数。例如(0,1,2,4的 MEX为3)
- 小美想知道将 a 数组全部清空的最小代价是多少,请你帮帮他吧
解析思路:
- 遍历数组使用freq[]数组记录每个数字出现的次数。初始mex=0;用while条件(如果mex在freq[mex]出现次数大于0,那么就将mex++),将当前mexN[i]记录成操作后的mex。然后遍历数组,分别计算删除几个后全部删除的代价总数,并替换最小的值,最终得到最小代价
- 自测通过,符合逻辑,不理解为什么为0%;优化后50%,复杂度太高,运行超时;上面为什么不通过的原因可能为输入数值过大,无法接收为int。理解C++题解含义后,优化Java代码,通过率100%
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for (int i = 0; i < t; i++) {
int n = in.nextInt();
long k = in.nextInt();// 不要用int,否则输入数值过大无法处理,通过率为0
long x = in.nextInt();
List<Integer> a = new ArrayList<>();
for (int j = 0; j < n; j++) {
a.add(in.nextInt());
}
long miniCost = getMiniCost(n, k, x, a);
System.out.println(miniCost);
}
}
public static long getMiniCost(int n, long k, long x, List<Integer> a) {
int[] mexN = new int[n + 1];
mexN[n] = 0;
int[] freq = new int[n + 1];// 记录每个数字出现的次数
for (int i = 0; i < n + 1; i++) {
freq[i] = 0;//初始化为0,后续出现一个,加1
}
int mex = 0;
for (int i = n - 1; i >= 0; i--) {
int p = a.get(i);// 取数组内的数
if (p <= n) {// 防止越界
freq[p] ++;// 将数放放到已出现的数组中,p代表数字,freq[p]代表p数字出现了几次
}
// 如果mex已经出现过,即frep[mex]>0,则++
while (freq[mex] > 0) {
mex ++;
}
mexN[i] = mex;
}
long miniCost = n * x;// 初始化为全部删除首个的情况
for (int i = 0; i <= n; i++) {
long cost = i * x + k * mexN[i];
if (cost < miniCost) {
miniCost = cost;
}
}
return miniCost;
}
}
7.小红的数字串
题目:
- 输入一个数字串(1~9的数字),输入一个正整数,从数字串中任意截取子串,要求子串表示的数字小于给定的正整数
- 计算有多少种这样的截取方式
解题思路:
- 双层循环,第一层从0开始,第二层从i+1开始,内层循环内使用subString(i,j)转换成long,判断是否小于给定k,小于则计数,大于则停止内层循环,开始下一个i的往后截取,遍历完成后累计判断小于k的即总的方案数。
- 注意:subString能截取到(n-1,n)即为最后一位,所以内层循环的边界是j<=n
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String numStr = in.nextLine();
Long k = in.nextLong();
int res = getKinds(numStr, k);
System.out.println(res);
}
private static int getKinds(String numStr, Long k) {
int count = 0;
int n = numStr.length();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
String spiltStr = numStr.substring(i, j);
Long spiltNum = Long.parseLong(spiltStr);
if (spiltNum < k) {
count++;
} else {
break;
}
}
}
return count;
}
}
8.小红的01串
题目:
- 小红拿到了一个01串。所谓01串,指仅由’0’和’1’两种字符组成的字符串。 小红可以进行任意次以下操作:选择字符串的一个字符,将其取反(‘0’变’1’或者’1’变0’)
- 小红定义一个01串为好串,当且仅当该01串不包含"010"子串和"101"子串,例如,"1001"是好串,但"100100“则不是好串。小红想知道,自己最少操作多少次可以将给定字符串变成好串?
解题思路:
- 瞎算逻辑:用while(如果不是好串),那么就去找到坏的最小索引,将这个坏的最后一位取反(用x^1,异或,相同为0,不同为1,或者用x也可以,是取反符号),然后截取擦操作后的数再拼接坏索引之后的数值,循环,直到最终变成好串
- 瞎算的,不符合逻辑
通过率:18.18%
代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
int opTime = operateStr(str);
System.out.println(opTime);
}
public static int operateStr(String str) {
int opTime = 0;
if (isGood(str)) {
return 0;
}
while (!isGood(str)) {
int index010 = str.indexOf("010");
int index101 = str.indexOf("101");
int miniBadIndex = -1; // 最小坏的索引
if (index010 > -1 && index101 > -1) {
miniBadIndex = Math.min(index010, index101);
} else if (index010 > -1) {
miniBadIndex = index010;
} else if (index101 > -1) {
miniBadIndex = index101;
}
// 将最后一个值取反
int opIndex = miniBadIndex +2;
int opNum = Integer.parseInt(str.substring(opIndex, opIndex +1));
// int afterOp = opNum ^1;// 异或取反,与1对比,相同为0,不同为1
int afterOp = ~opNum;//取反符号
opTime++;
str = String.valueOf(afterOp).concat(str.substring(opIndex));
}
return opTime;
}
// 是否为好串
public static boolean isGood(String str) {
if ((str.indexOf("010") > 0) || (str.indexOf("101") > -1)) {
return false;
} else {
return true;
}
}
}
10.小红的拼图
题目:
- 小红正在玩一个拼图游戏,她有一些完全相同的拼图组件:小红准备用这些组件来拼成一些图案。 这些组件可以通过顺时针旋转90度、180度、270度变成不同的形态。我们定义旋转0度用字符"W"表示,旋转90度用字符’D’表示,旋转180度用字符’S’表示,旋转270度用字符’A’表示:
- 小红准备用这些组件来拼成一些图案。这些组件可以通过顺时针旋转90度、180度、270度变成不同的形态。小红想知道,自己是否能按照给定的规则拼出图形?
- 请注意:若两个组件相邻,那么必须凹进去的和凸出来的完美契合!“凸凸”和”凹凹”的相邻都是不合法的。
解题思路:
- 对照图形,将水平不能连接的图形对应字母放到一个list中,再将垂直不能连接的图形字放到一个list中,。先遍历字符串的每行,判断每行是否包含水平不允许相连的字符,包含,返回No。每行遍历完成后,再将行转成列,再遍历每列是否包含垂直不允许相连的字符串
- 枚举暴力求解
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for (int i = 0; i < t; i++) {
int n = in.nextInt();
int m = in.nextInt();
in.nextLine();
String[] str = new String[n];
for (int j = 0; j < n; j++) {
str[j] = in.nextLine();
}
String valid = getYesOrNo(str, n, m);
System.out.println(valid);
}
}
public static String getYesOrNo(String[] str, int n, int m) {
// 水平不允许连接的图形
List<String> spNos = new ArrayList<>();
spNos.add("WS");
spNos.add("SW");
spNos.add("WA");
spNos.add("SW");
spNos.add("DS");
spNos.add("DA");
spNos.add("SA");
spNos.add("AD");
for (int i = 0; i < n; i++) {
for (String spNo : spNos) {
// 如果行字符中包含水平不允许直连的图形,则为"No"
if (str[i].indexOf(spNo) != -1) {
return "No";
}
}
}
if (n < 2) {
return "Yes";
}
// 将数组转成行转列
String[] tranStr = new String[m];
for (int i = 0; i < m; i++) {
tranStr[i] = "";
for (int j = 0; j < n; j++) {
tranStr[i] = tranStr[i].concat(str[j].substring(i, i + 1));
}
}
// 垂直不允许连接的图形
List<String> czNos = new ArrayList<>();
czNos.add("DW");
czNos.add("SW");
czNos.add("WS");
czNos.add("AW");
czNos.add("AD");
czNos.add("SA");
czNos.add("SD");
for (int i = 0; i < m; i++) {
for (String czNo : czNos) {
// 如果行字符中包含垂直不允许直连的图形,则为"No"
if (tranStr[i].indexOf(czNo) != -1) {
return "No";
}
}
}
return "Yes";
}
}
11.小红的奇偶抽取
题目:
- 小红拿到了一个正整数,她希望把数位中的奇数和偶数分别抽取出来然后做差,请你求出这个差的绝对值。
- 例如,302938的奇数抽取出来是393,偶数抽取出来是28,最终的差的绝对值是365。
解题思路:
- 用StringBuild选取字符串中奇数和偶数字符分别做.append操作,最终结果转换成long计算绝对值
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String nStr = in.nextLine().trim();
Long res = getRes(nStr);
System.out.println(res);
}
private static Long getRes(String nStr) {
StringBuilder jiStr = new StringBuilder();
StringBuilder ouStr = new StringBuilder();
for (int i = 0; i < nStr.length(); i++) {
String s = nStr.substring(i, i + 1);
Long num = Long.parseLong(s);
if (num % 2 == 0) {
ouStr.append(num);
} else {
jiStr.append(num);
}
}
Long ji = 0L;
Long ou = 0L;
if (jiStr.length() > 0) {
ji = Long.parseLong(jiStr.toString());
}
if (ouStr.length() > 0) {
ou = Long.parseLong(ouStr.toString());
}
Long res = ji - ou;
if (res < 0) {
res = ou - ji;
}
return res;
}
}
12.游游的整数切割
题目:
- 游游拿到了一个正整数,她希望将它切割成两部分,使得它们的和为偶数。游游想知道有多少种合法的切割方案?
- 注:切割后的正整数允许出现前导零。
解题思路:
- 截取字符串最后一位数字b = Integer.parseInt(s.substring(n - 1)),遍历字符串取每一个数字a = Integer.parseInt(s.substring(i - 1, i));,用(a+b)%2==0即为和偶数,count++,遍历完成,输出count即为所有满足条件的截取方案
- 注意数值越界问题,只取最后一位进行计算。如果直接用左侧截取和右侧相加,遇到超长字符串会无法用int或long接收数值计算,所以只取左侧和右侧的最后一位数字进行运算也可以保证是否为偶数校验
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s = in.nextLine();
int count = getCount(s);
System.out.println(count);
}
public static int getCount(String s) {
int count = 0;
if (s.length() < 2) {
return 0;
}
//直接使用字符串最后一位
int b = Integer.parseInt(s.substring(s.length() - 1));
for (int i = 1; i < s.length(); i++) {
// 只使用最后一位相加即可,否则会出现数值过大越界情况
int a = Integer.parseInt(s.substring(i - 1, i));
if ((a + b) % 2 == 0) {
count++;
}
}
return count;
}
}
14.小美走公路
题目:
- 有一个环形的公路,上面共有n站,现在给定了顺时针第站到第i+1站之间的距离(特殊的,也给出了第n站到第1站的距离)。
- 小美想沿着公路第x站走到第y站,她想知道最短的距离是多少?
解题思路:
- 先遍历计算出总的路程,然后计算顺时针从小到大的路程,使用Math.min(顺时针路程,总路程-顺时针路程),得到结果,通过率50%
- 输入用例数组和输出结果类型用long,不要用int,不然数值过大,只能通过50%
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
long[] a = new long[n];
for (int i = 0; i < n; i++) {
a[i] = in.nextLong();
}
int x = in.nextInt();
int y = in.nextInt();
long miniDis = miniDistance(a, x, y, n);
System.out.println(miniDis);
}
public static long miniDistance(long[] a, int x, int y, int n) {
long miniDis = 0;
if (n <= 1) {
return miniDis;
}
if (x == y) {
return miniDis;
}
int start = x - 1;
int end = y - 1;
if (x > y) {
int temp = start;
start = end;
end = temp;
}
long totalDis = 0;
for (int i = 0; i < n; i++) {
totalDis = totalDis + a[i];
}
long shunDis = 0;
for (int i = start; i < end; i++) {
shunDis = shunDis + a[i];
}
miniDis = Math.min(shunDis, totalDis - shunDis);
return miniDis;
}
}
15.小美的排列询问
题目:
- 小美拿到一个序列,想知道其中某两个数是否相邻
解析思路:
- 遍历数组,判断上一个和当前字符是否满足xy或yx,只要有一个满足即可返回Yes,否则为No
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n = in.nextInt();
int[] nums = new int[n];
int[] xy = new int[2];
for (int i=0;i<n;i++) {
int x = in.nextInt();
nums[i] = x;
}
for (int i=0;i<2;i++) {
int x = in.nextInt();
xy[i] = x;
}
boolean res = xyRelated(n, nums, xy);
String print = "No";
if (res) {
print = "Yes";
}
System.out.println(print);
}
private static boolean xyRelated(int n, int[] nums, int[] xy) {
int a=nums[0];
for(int i=1;i<n;i++) {
if(xy[0] == a && xy[1] == nums[i]) {
return true;
}
if(xy[0] == nums[i] && xy[1] == a) {
return true;
}
a = nums[i];
}
return false;
}
}
19.相遇
题目:
- 将街道看做一维数组,输入一个n,表示有n个行人,再按行人输入每个行人初始位置和行走方向。假设行走方向为0,则向左,为1,则向右,行人速度相同且初始位置唯一。如果某一刻位置相同,则成为第一次相遇
- 求问共有多少对行人会相遇?
解题思路:
- 用二维数组a[n][2]存储每个人的位置a[i][0]和朝向a[i][1],并用Arrays.sort(x, (a, b) -> a[0] - b[0]);对初始位置进行升序排序。1.粗暴方法:双层遍历,如果i是向右走,则遍历i+1~n,出现向左走,即可相遇,count++,复杂度较高,严重超时通过率只有46.67%。
- 先循环一次计算总的向左的个数。然后再遍历,如果是向左,则累计当前向左的总个数leftCount,否则向右,则meetingCount += (totalLeft - leftCount);(totalLeft - leftCount)表示当前右侧剩下的向左走的,即为相遇的条件。遍历完成后meetingCount即为次数,通过率86.67%。
通过率:86.67%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[][] x = new int[n][2];
for (int i = 0; i < n; i++) {
int a = in.nextInt();//位置
int b = in.nextInt();//方向
x[i][0] = a;
x[i][1] = b;
}
// 按大小排序
// Arrays.sort(x, new Comparator<int[]>() {
// @Override
// public int compare(int[] a, int[] b) {
// if (a[0] > b[0]) {
// return 1;
// } else if (a[0] < b[0]) {
// return -1;
// } else {
// return 0;
// }
// }
// });
Arrays.sort(x, (a, b) -> a[0] - b[0]);
int count = getCountLow2(x, n);
System.out.println(count);
}
// 此算法复杂度为O(n平方),严重超时,通过率仅46.67%
public static int getCount(int[][] x, int n) {
int count = 0;
// 排序后从小到大处理向右方向的,但凡遍历一个向左的即相遇
for (int i = 0; i < n - 1; i++) {
if (x[i][1] == 1) {
for (int j = i + 1; j < n; j++) {
if (x[j][1] == 0) {
count++;
}
}
}
}
return count;
}
// 此算法复杂度为O(n),预处理0集合,用例通过率86.67%
public static int getCountLow1(int[][] x, int n) {
int count = 0;
// prefixZero[i] 表示 x[i..n-1] 中 0 的数量
int[] prefixZero = new int[n + 1];
// 预处理前缀和
for (int i = n - 1; i >= 0; i--) {
prefixZero[i] = prefixZero[i + 1] + (x[i][1] == 0 ? 1 : 0);
}
// 排序后从小到大处理向右方向的,但凡遍历一个向左的即相遇
for (int i = 0; i < n - 1; i++) {
if (x[i][1] == 1) {
count = count + prefixZero[i + 1];
}
}
return count;
}
// 通过率还是86.67%
public static int getCountLow2(int[][] x, int n) {
int meetingCount = 0;
int totalLeft = 0;
for (int i = 0; i < n; i++) {
if (x[i][1] == 0) {
totalLeft++;
}
}
int leftCount = 0;
for (int i = 0; i < n; i++) {
if (x[i][1] == 0) {
leftCount++;
} else {
meetingCount += (totalLeft - leftCount);
}
}
return meetingCount;
}
}
21.小红的与运算
题目:
-
小红拿到了一个数组,她想在其中选择k个数,使得这k个数的按位与&运算的值尽可能大。你能帮帮她吗?
-
初始最大值为0,循环30位次,用1<<bit将值变成1…位,然后与最大值进行或操作,取出所有1为temp,遍历数值数组,取出当前数与temp进行&操作,如果操作结果为temp,则将次数加入可运算内,如果累计达到k,那么就替换最大值为当前temp解题思路
-
与运算&,运算二者位都是1,结果位为1。
-
或运算|,运算二者其中一个位1,即为1。
-
左移<<:将数值左移多少位,比如1<<2,即为100;
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n = in.nextInt();
int k = in.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = in.nextInt();
}
int max = getMax(n, k, a);
System.out.println(max);
}
public static int getMax(int n, int k, int[] a) {
int maxAnd = 0;
for (int bit = 30; bit >= 0; bit--) {
// 将第bit位置为1
int temp = maxAnd | (1 << bit);
int count = 0;
for (int num : a) {
if ((num & temp) == temp) {
count++;
}
}
if (count >= k) {
maxAnd = temp;
}
}
return maxAnd;
}
}
24.小欧的括号操作
题目:
- 小欧拿到了一个只包含字符’(’和’)’的字符串,她有以下两种操作:
- 1.用’(’代替一对相邻的括号(“()”)。
- 2.用’)’代替一对相邻的括号(“()”)
- 请注意,只有相邻的括号字符才可以操作。小欧想知道,若干次操作以后,该字符串的最短长度是多少?
解题思路:
- 1如果不包含"()",则返回长度。遍历字符串,如果i>0,如果上一个字符为(且当前字符和下一字符满足(),则替换成),然后递归调用,并break;如果不满足以上条件,则 判断当前字符和下一字符满足(),替换成(,递归调用并break;。直到最终没有包含“()”,返回长度。以上递归调用复杂度较高,运行超时
- 替代递归方式代码简洁度提升,但是一样的超时,还是53.33。用while(str.contain("()))只要包含就开始替换,用indexOf找到第一个()出现的索引,并判断前一个是否是(,如果是则替换为),否则替换为(,然后循环替换。不理解为什么超时
- 复杂度过高,运行超时
- 注意:str.replaceFirst(“\(\)”, “)”);中要用\(\),因为()会被解析成正则表达式,是特殊字符
通过率:55.33%
代码:
import java.util.Scanner;
public class Main24 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
int count = getStrLength(str);
System.out.println(count);
}
// 代码简洁度提升,但是通过率还是55.33%,超时,不理解
public static int getStrLength(String str) {
int length = str.length();
while (str.contains("()")) {
int index = str.indexOf("()");//出现()的首个索引
if (index > 0) {
String p = str.substring(index - 1, index);
if (p.equals("(")) {
str = str.replaceFirst("\\(\\)", ")");
length = str.length();
continue;
}
}
str = str.replaceFirst("\\(\\)", "(");
length = str.length();
}
return length;
// // 通过率55.33%,复杂度过高,超时
// public static int getStrLength(String str) {
// int length = str.length();
// if (!str.contains("()")) {
// return length;
// }
//
// for (int i = 0; i < str.length() - 1; i++) {
// String x = str.substring(i, i + 1);
// String y = str.substring(i + 1, i + 2);
// if (i > 0) {
// String p = str.substring(i - 1, i);
// if (p.equals("(") && x.equals("(") && y.equals(")")) {
//// String replaceStr = replace(i, str, ")");
// // 此处不能用replaceFirst("()", ")");因为"()"会被解析成正则表达式,表示捕获分组,需要将其进行转义\\
// String replaceStr = str.replaceFirst("\\(\\)", ")");
// length = replaceStr.length();
// length = getStrLength(replaceStr);
// break;
// }
// }
// if (x.equals("(") && y.equals(")")) {
//// String replaceStr = replace(i, str, "(");
// String replaceStr = str.replaceFirst("\\(\\)", "(");
// length = replaceStr.length();
// length = getStrLength(replaceStr);
// break;
// }
// }
// return length;
// }
// 手动替换
public static String replace(int index, String str, String replaceMent) {
StringBuffer replaceStr = new StringBuffer();
String before = str.substring(0, index);
replaceStr.append(before);
replaceStr.append(replaceMent);
String after = str.substring(index + 2);
replaceStr.append(after);
return replaceStr.toString();
}
}
25.小欧的选数乘积
题目:
- 小欧拿到了两个正整数x和y,她想进行一些操作,使得x不小于y。每一轮操作:从给定的整数数组 a 中选定一个元素 αi,将x乘以 ai,并删掉 a 数组中所有的等于 ai的数字。
- 小欧想知道,自己最少进行多少轮操作,才可以使得 x不小于 y?
解题思路:
- 将给定数组用List存储,并用sort降序排序。遍历list,从大的开始取用,并用Set来存储已经用过的数字,遍历之前如果set已经包含当前数字,则跳过(模拟使用过就删除的场景),不包含则用x*i,并将使用过的数字添加到set中,并累计操作次数。如果已经大于给定k值,则break,即为操作最小次数
- 注意:输入用long型,输入用例较大,用int只能通过61.54%
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
long x = in.nextLong();
long y = in.nextLong();
int n = in.nextInt();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
int one = in.nextInt();
list.add(one);
}
int count = operate(x, y, list);
System.out.println(count);
}
private static int operate(long x, long y, List<Integer> list) {
if (x >= y) {
return 0;
}
int count = 0;
Set<Integer> setA = new HashSet();
list.sort((a, b) -> b.compareTo(a));
for (int i = 0; i < list.size(); i++) {
if (setA.contains(list.get(i))) {
continue;
}
x = x * list.get(i);
count++;
setA.add(list.get(i));
if (x >= y) {
break;
}
}
if (x < y) {
count = -1;
}
return count;
}
}
29.小球投盒
题目:
- 小红一共有n 个盒子,标号为1 到n,小红向盒子里放入小球 m 次,每次进行以下两个操作中的一个:
- 1.向编号为 : 的盒子里放入一个小球;
- 2.向除了编号为 : 的其他 n -1 盒子里放入一个小球。
- 小红想知道,第几次操作之后,所有盒子里至少都有一个小球,如果一直无法达到这个目标输出 -1。
- 输入描述: 第一行两个整数 n 和 m,表示盒子的数量和操作的次数。 接下来 m 行,每行两个整数ti和xi;,表示第i次操作的类型和x的值。 l ≤n,m{<} 105, 1≤ti{<}2,1{<}ri{<}n
- 输出描述:输出一个整数,表示第几次操作之后,所有盒子里至少都有一个小球,如果一直无法达到这个目标,输出 -1。
解题思路:
通过率:19/30,复杂度太高,超时严重,63.33%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
Set<Integer> set = new HashSet<>();
for (int i = 1; i <= n; i++) {
set.add(i);
}
int times = 0;
for (int i = 0; i < m; i++) {
int type = in.nextInt();
int x = in.nextInt();
if (type == 1) {
if (set.contains(x)) {
set.remove(x);
}
} else {
if (set.contains(x)) {
set.clear();
set.add(x);
} else {
set.clear();
}
}
times++;
if (set.isEmpty()) {
break;
}
}
if (set.isEmpty()) {
System.out.println(times);
} else {
System.out.println(-1);
}
}
}
30.数字变换
题目:
- 给出两个数字 a,b,a每次可以乘上一个大于1的正整数得到新的 a。我们将这个操作叫做一次它乘。现在请你计算,a是否可以通过若干次它乘变为 b。
- 若 a 可以通过若干次它乘变为 b,请你计算最多可以经过多少次它乘;若 a不可能变为 b,输出 -1 即可。
解题思路:
- 如果a不是b的因子(b%a!=0),则a不可能变成b,直接返回-1,否则,先用b/a得到剩下的数,然后计算剩下的数的最小公因子的个数,即为最终结果
- 计算n的公因子的时候边界条件用while(i<=Math.sqrt(n)),i初始为2,每次都用n/i,如果不能n%i==0,则将i++,表示当前i已经不能满足
通过率:100%
代码:
import java.util.*;
import java.lang.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int a = in.nextInt();
int b = in.nextInt();
int count = getCount(a, b);
System.out.println(count);
}
public static int getCount(int a, int b) {
int count = 0;
if (a <= 0) {
return -1;
}
if (b % a != 0) {
return - 1;
}
count ++;
int n = b / a;
count = count + divide(n);
return count;
}
public static int divide(int n) {
int count = 0;
if (n <= 2) {
return count;
}
int i = 2;
while (i <= Math.sqrt(n)) {
if (n % i == 0) {
n = n / i;
count++;
} else{
i++;
}
}
return count;
}
}
31.二叉树
题目:
解题思路:
- 注意:取模数long mod = 1000000007L;取模含义则是用结果res%mod
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
long count = getCount(n, m);
System.out.println(count);
}
public static long getCount(int n, int m) {
long mod = 1000000007L;
// dp[i][j]表示i个节点不超过j高度的二叉树方案数量
long[][] dp = new long[n + 1][m + 1];
// 初始化空树方案为1,即n为0
for (int j = 0; j <= m; j++) {
dp[0][j] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = 0;
// 分配i-1个节点给左右子树
for (int k = 0; k < i; k++) {
dp[i][j] = (dp[i][j] + dp[k][j - 1] * dp[i - 1 - k][j - 1]) % mod;
}
}
}
return dp[n][m];
}
}
33.树上最短路
题目:
- 牛牛刚刚学了二叉树和最短路。他现在很好奇,对于一棵完全二叉树(对于有孩子的节点i,其左孩子为2讠,右孩子为2i+ 1)。
- 给定的两个点之间的最短路的长度是多少?我们认为相邻两点之间的距离为1
解题思路:
- 使用while,当a、b不相等时,进行操作,a>b,则a/2(即找到上层节点),累计距离+1,b<a,则用b/2,一直到a=b在同一个祖先节点上,a操作的次数加上b操作的次数,则为a、b之间的最短路径
- 复杂度较高,有超时风险
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int t = in.nextInt();
for (int i = 0; i < t; i++) {
int a = in.nextInt();
int b = in.nextInt();
System.out.println(calculateDistance(a, b));
}
}
public static int calculateDistance(int a, int b) {
int distance = 0;
while (a != b) {
if (a > b) {
a /= 2;
distance++;
} else {
b /= 2;
distance++;
}
}
return distance;
}
}
34.游游的字母串
题目:
- 对于一个小写字母而言,游游可以通过一次操作把这个字母变成相邻的字母。 'a’和"b’相邻,"b’和’相邻,以此类推。特殊的,"a’和’z’也是相邻的。可以认为,小写字母的相邻规则为一个环。
- 游游拿到了一个仅包含小写字母的字符串,她想知道,使得所有字母都相等至少要多少次操作?
解题思路:
- 双层遍历字符数组,外层为目标字符,内层为将所有变成目标字符的操作数计算。用Set存储已经成为过目标字符的,如果重复则跳过,内层循环字符如果与目标字符相同也跳过,然后计算当前字符变成目标字符的最小操作数。正常操作为temp=Math.abs(cur-target),环形跨越操作为26-temp,对比两种操作选最小即为当前字符变成目标字符的操作数。一个目标字符全部遍历计算完成后历史目标字符对比,如果值小于历史值,则更新为当前目标操作数,即为最小操作数
- 注意:正常距离Math.abs(cur-target)和跨越环形距离用26-Math.abs(cur-target)。然后用Math.min()取较小值
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
char[] chars = str.toCharArray();
int count = operate(chars);
System.out.println(count);
}
public static int operate(char[] chars) {
int totalOperations = -1;
Set<Character> set = new HashSet<>();// 存储已经计算过的目标字符
for (char target : chars) {
if (set.contains(target)) {
continue;// 如果包含,则不用重复计算
}
int total = 0;
for (char cur : chars) {
if (cur == target) {
continue;// 如果与目标字符相等,则无需操作
}
int temp = Math.abs(cur -
target);// 计算当前字符与目标字符之间的距离
int opTime = Math.min(26 - temp,
temp);// 计算目标距离与跨越环形之后的距离最小值
total = total + opTime;// 累计每个字符变成目标字符的操作数
}
if (totalOperations == -1) {
totalOperations = total;// 每次取最小的操作数
} else {
totalOperations = Math.min(total, totalOperations);
}
set.add(target);
}
return totalOperations;
}
}
36.活动安排
题目:
- 给定几个活动,每个活动安排的时间为a:6)。求最多可以选择多少个活动,满足选择的活动时间两两之间没有重合。
- 输入描述:第一行输入一个整数n(1 {<} n {<} 2.105),表示可选活动个数。 接下来的n行,每行输入两个整数ai,b;(0≤ ai{<} b;≤ 109),表示第个活动的时间。
解题思路:
- 先将活动结束时间按升序排序,然后遍历活动,如果开始时间大于上一个结束时间,那么就添加到可选活动内,每次都选结束时间最早的活动,以便能选择更多的活动。用lastEnd = -1表示初始结束时间,每次添加活动后更新当前结束时间
- 运行时间较长,有超时风险
通过率:100%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[][] act = new int[n][2];
for (int i = 0; i < n; i++) {
act[i][0] = in.nextInt();
act[i][1] = in.nextInt();
}
int count = getChoose(n, act);
System.out.println(count);
}
public static int getChoose(int n, int[][] act) {
int count = 0;
// 先按活动结束时间升序排序
Arrays.sort(act, (a, b) ->(a[1] - b[1]));
int lastEnd = -1;
for (int i = 0; i < n; i++) {
// 每次选择结束时间最早的活动,以便能选更多的活动
if (act[i][0] >= lastEnd) {
count ++;
lastEnd = act[i][1];
}
}
return count;
}
}
37.牛牛的Ackmann
题目:
- 牛牛最近了解到了著名的阿克曼(Ackmann)函数,阿克曼函数是一个增长极其迅速的函数,另外一个著名的数据结构–并查集的最优复杂度便可以达到阿克曼函数的反函数级别.
- 请你计算阿克曼函数的几个整数定义域的结果。
- Ackmann(m,n) = {
-
n+1,m=0 -
Ackmann(m-1,1),(m{>}0&n=0) -
Ackmann(m-1,Ackmann(m,n-l)),(m{>}0&n {>}0) - }
解题思路:
- 直接使用所给公式,进行分情况进行公式计算或者递归调用
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
int res = ackmann(m, n);
System.out.print(res);
}
public static int ackmann(int m, int n) {
int res = 0;
if (m == 0) {
res = n + 1;
}
if (m > 0 & n == 0) {
res = ackmann(m - 1, 1);
}
if (m > 0 & n > 0) {
res = ackmann(m - 1, ackmann(m, n - 1));
}
return res;
}
}
38.跳台阶
题目:
- 一只青蛙一次可以跳上1级台阶或2级台阶,求跳上n级台阶有多少种不同的跳法。
解析思路:
- 跳台阶算法的核心解法与斐波那契数列密切相关。
- 当青蛙每次可跳1级或2级台阶时,跳法的总数遵循斐波那契数列的递推规律,可通过动态规划、递归等方法实现
- 用a,b,c作临时变量,每次变换c=a+b,b=c,a=b,模拟f[n]=f[n-1]+f[n-2],降低复杂度
通过率:100%
代码:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int kinds = getKinds(n);
System.out.println(kinds);
}
private static int getKinds(int n) {
if(n <= 2) {
return n;
}
int a=1,b=2,c=0;
for(int i=3;i<=n;i++) {
c=a+b;
a=b;
b=c;
}
return c;
}
}
39.超级圣诞树
题目:
- 绘制圣诞树算法:输入圣诞树大小n
解题思路:
通过率:
代码:
40.小红的数组
题目:
- 小红拿到了一个长度为 n 的数组,数组中的元素都是正整数,小红想让你回答以下三个问题,取两个数乘积大于k的方案数、取两个数乘积等于 k 的方案数、取两个数乘积小于k的方案数。
- 注:两个数是不放回且同时取的。例如对于数组[1,2,3,4,5]而言,取[1,2]和[2,1]我们认为是同一种方案。但是,如果有两个数相等,那么取相等但位置不同的数不认为是同一种取法。例如对于数组[2,2,2,3]而言,有三种方案可以取到[2,2]。
解题思路:
- 双层循环,遍历当前数字,遍历与当前数字之后的数字依次相乘,按数值大小与k比较,然后累计大于k、小于k、等于k输出。
- 不理解,为什么没有全部通过,只有53.85%
通过率:53.85%
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
long n = in.nextLong();
long k = in.nextLong();
List<Integer> a = new ArrayList<>();
for (int i = 0; i < n; i++) {
a.add(in.nextInt());
}
int[] count = new int[3];
getCounts(n, k, a, count);
System.out.print(count[0] + " ");
System.out.print(count[1] + " ");
System.out.println(count[2]);
}
public static void getCounts(long n, long k, List<Integer> a, int[] count) {
count[0] = 0;// 大于k
count[1] = 0;// 等于k
count[2] = 0;// 小于k
if (n < 2) {
return;
}
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
long sum = a.get(i) * a.get(j);
if (sum > k) {
count[0] ++;
} else if (sum == k) {
count[1]++;
} else {
count[2]++;
}
}
}
}
}
41.添数博弈
题目:
- 小A和小B创造了一个叫添数博弈的游戏,游戏规则如下:
- 首先在数池中添加两个整数,y,接下来,两个人轮流开始添数;添数的规则是在数池中任意选择两个数x,y,令m=|x-y|.
- 若m不在数池中,则将m添加到数池中;若m已经在数池中,则游戏结束。
- 假设小A先选数,两人轮流操作添数,若有一方不能无法添数时,则该人就输了。
- 输入描述:输入的第一行给出测试用例的数量T(1 {<} T {<} 10);随后T行,每行给出两个整数x,y(1 ≤ x,y≤ 10^9) x≠y
解题思路:
- 直接模拟游戏过程进行递归调用
- 复杂度过高,运行超时
通过率:42.86%,严重超时
代码:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
for (int i = 0; i < t; i++) {
int x = in.nextInt();
int y = in.nextInt();
String win = gameWin(x, y);
System.out.println(win);
}
}
public static String gameWin(int x, int y) {
String win = "A";
List<Integer> list = new ArrayList<>();
list.add(x);
list.add(y);
int initM = Math.abs(x - y);
if (list.contains(initM)) {
win = "B";
return win;// 小A先操作,如果初始无法插入,则小B赢
}
list.add(initM);
Map<String, Integer> map = new HashMap<>();
map.put("01", initM);
boolean[] end = new boolean[1];
end[0] = false;
boolean lastA = true;// 是否A胜
boolean lastWina = getWIn(list, map, end, lastA);
if (lastWina) {
win = "A";
} else {
win = "B";
}
return win;
}
public static boolean getWIn(List<Integer> list, Map<String, Integer> map,
boolean[] end, boolean lastWina) {
boolean push = false;
for (int i = 0; i < list.size() - 1; i++) {
for (int j = i + 1; j < list.size(); j++) {
String key = String.valueOf(i).concat(String.valueOf(j));
if (map.containsKey(key)) {
continue;
}
int m = Math.abs(list.get(i) - list.get(j));
if (!list.contains(m)) {
list.add(m);
lastWina = !lastWina;
push = true;
map.put(key, m);
break;
}
}
if (push) {
break;
}
}
if (!push) {
end[0] = true;
return lastWina;
} else {
boolean res = getWIn(list, map, end, lastWina);
if (end[0]) {
return res;
}
}
return lastWina;
}
}
42.吃糖果
题目:
- 牛妹喜欢吃糖,现在有一排共 几个糖果,第i个糖果具有一个甜度值 ai。因为吃甜食太多了会得蛀牙,所以牛妹吃的糖果的甜度值总和不能超过 k。
- 她可以以任意的顺序吃糖,请问她最多能吃多少个糖果?
解题思路:
- 用List存储甜度,按照升序排序,然后遍历,从糖度最低的开始计数,并累加甜度,当甜度大于给定总值,即停止遍历,得到可吃的最多糖果
通过率:100%
代码:
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n = in.nextInt();
int k = in.nextInt();
List<Integer> sweets = new ArrayList<>();
for (int i = 0; i < n; i++) {
int x = in.nextInt();
sweets.add(x);
}
int count = getSweets(k, sweets);
System.out.println(count);
}
private static int getSweets(int k, List<Integer> sweets) {
int count = 0;
int degree = 0;
sweets.sort((a, b)->a.compareTo(b));
for (int i = 0; i < sweets.size(); i++) {
degree = degree + sweets.get(i);
if(degree < k) {
count++;
} else{
break;
}
}
return count;
}
}
43.开心还是难过
题目:
- 牛牛经常用": - )“表示开心,用”: - ("表示难过;
- 现在输入牛牛发出的一句话,请你判断牛牛是否开心?
解题思路:
- 遍历字符串,用subString(i,i+3)截取三个字符,看是否满足happy或者sad,进行累加,遍历完成后根据happy和sad的数量比较判断心情
通过率:100%
代码:
import java.util.Scanner;
public class Main43 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
String str = in.nextLine();
String res = getRes(str);
System.out.println(res);
}
private static String getRes(String s) {
String res = "";
int happy = 0;
int sad = 0;
for (int i = 0; i < s.length() - 2; i++) {
String spiltStr = s.substring(i, i + 3);
if (spiltStr.equals(":-)")) {
happy++;
} else if (spiltStr.equals(":-(")) {
sad++;
}
}
if (happy == 0 && sad == 0) {
res = "None";
} else if (happy > sad) {
res = "Happy";
} else if (happy == sad) {
res = "Just so so";
} else if (happy < sad) {
res = "Sad";
} else {
res = "";
}
return res;
}
}
44.数字游戏
题目:
- dd在玩数字游戏,首先他拿到一个x。当x不为零时进行如下操作:
- 如果二进制必中有奇数个1,则x二进制形式下最低位取反(即0变成1,1变成0)
- 如果二进制必中有偶数个1,则必二进制形式下非前导零最高位取反
- 询问对于一个x,操作几次后变为零
解题思路:
- 用while条件,如果x不等于0,执行操作,计算1的个数,如果为奇数,则用x^ 1异或低位取反,如果为偶数,则用Integer.highestOneBit(x);先获取x最高位1(如1000),然后将x^最高位1,异或高位取反,每次操作step++,最终x变成0,所得step即为结果
- x^ 1,相当于x的最后一位是1则为0,是0则为1,满足异或取反。x^ Integer.highestOneBit(x);假设x为1011相当于1011^1000,相同为0,不同为1,结果为0011,符合高位取反
通过率:100%
代码:
import java.util.*;
public class Main44 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int x = in.nextInt();
int count = operate(x);
System.out.println(count);
}
}
// 复杂度优化,通过率100%
public static int operate(int x) {
int steps = 0;
while (x != 0) {
int ones = countOnes(x);
if (ones % 2 == 1) {
// 奇数个 1:翻转最低位
x ^= 1;
} else {
// 偶数个 1:翻转最高非零位
int highestBit = Integer.highestOneBit(x);
x ^= highestBit;
}
steps++;
}
return steps;
}
// 统计 1 的个数(Brian Kernighan 算法)
public static int countOnes(int x) {
int count = 0;
while (x != 0) {
x &= (x - 1); // 清除最低位的 1
count++;
}
return count;
}
// 复杂度太高,通过率77.78%
// public static int operate(int x) {
// String binary = Integer.toBinaryString(x);
// int steps = 0;
// while (!binary.equals("0")) {
// steps++;
// int countOnes = countOnes(binary);
// if (countOnes % 2 == 1) {
// // 奇数个 1,翻转最低位
// char lastBit = binary.charAt(binary.length() - 1);
// binary = binary.substring(0,
// binary.length() - 1) + (lastBit == '0' ? '1' : '0');
// } else {
// // 偶数个 1,翻转非前导 0 的最高位
// int firstNonZeroIndex = findFirstNonZero(binary);
// if (firstNonZeroIndex != -1) {
// char bitToFlip = binary.charAt(firstNonZeroIndex);
// binary = binary.substring(0,
// firstNonZeroIndex) + (bitToFlip == '0' ? '1' : '0') + binary.substring(
// firstNonZeroIndex + 1);
// }
// }
// // 去除前导 0(如果存在)
// binary = binary.replaceFirst("^0+(?!$)", "");
// }
// return steps;
// }
//
// // 统计二进制字符串中 1 的个数
// private static int countOnes(String binary) {
// int count = 0;
// for (char c : binary.toCharArray()) {
// if (c == '1') {
// count++;
// }
// }
// return count;
// }
//
// // 找到第一个非 0 的位(从左到右)
// private static int findFirstNonZero(String binary) {
// for (int i = 0; i < binary.length(); i++) {
// if (binary.charAt(i) != '0') {
// return i;
// }
// }
// return -1; // 全 0 的情况
// }
}
45.对称之美
题目:
- 给出n个字符串,从第1个字符串一直到第n个字符串每个串取一个字母来构成一个新字符串,新字符串的第i个字母只能从第i行的字符串中选出。这样就得到了一个新的长度为n的字符串,
- 请问这个字符串是否有可能为回文字符串?
- 输入描述:第一行一个数字 t,1 {<} t{<} 50,代表测试数据的组数。 每组测试数据先给出一个数字n,然后接下来n行每行一个只由小写字母组成的字符串 Si。1 ≤n{<} 100,1≤ si{<} 50
解题思路:
- 用第i个和倒数第i个字符串匹配是否有相同字符,便利到n/2。公共字符匹配,用字符串长度短的进行遍历,在长字符串中使用index.of(短字符)!=-1即能找到匹配的,找到后直接break,n/2个遍历都能找到公共字符,则返回Yes,有一个不匹配即为No
- 注意:nextInt() 后如果需要调用 nextLine() ,会直接读取本行换行符\n,读取值为空行,即为null。需要在 nextInt() 后额外调用一次 nextLine(),手动消耗掉缓冲区中的换行符,再进行正常的 nextLine() 读取输入。
通过率:100%
代码:
import java.util.Scanner;
public class Main45 {
// 100%通过率
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();// 给定t组需要计算的数据
for (int i = 0; i < t; i++) {
// nextInt() 只读取整数本身,而不会读取它后面的换行符(\n),这个换行符会留在输入缓冲区中。
// 当后续调用 nextLine() 时,它会直接读取这个换行符,认为已经读取了一行(空行),从而跳过了你原本想输入的字符串。
int n = in.nextInt();// 每组字符串个数为n
// 解决方法:在 nextInt() 后额外调用一次 nextLine(),手动消耗掉缓冲区中的换行符,让下一次 nextLine() 正常读取用户输入。
in.nextLine();
String[] strs = new String[n];
for (int j = 0; j < n; j++) {
strs[j] = in.nextLine();
}
String res = getYesOrNo(n, strs);
System.out.println(res);
}
}
public static String getYesOrNo(int n, String[] strs) {
String res = "Yes";
if (n == 1) {
res = "Yes";
return res;
}
for (int i = 0; i < n / 2; i++) {
// 当前i的和倒数i进行匹配是否有相同字符串
boolean flag = hasCommonStr(strs[i], strs[n - 1 - i]);
if (!flag) {
return "No";
}
}
return res;
}
public static boolean hasCommonStr(String a, String b) {
boolean flag = false;
String mini = a;
String max = b;
if (a.length() > b.length()) {
max = a;
mini = b;
}
for (char c : mini.toCharArray()) {
if (max.indexOf(c) != -1) {
flag = true;
break;
}
}
return flag;
}
// 以下代码错误,问题解析错误,误以为每一行就一个字母,实则不是,每一行是一个字符串,适用于解给定一串字符,判断是否能构成回文的题解
// public static void main(String[] args) {
// Scanner in = new Scanner(System.in);
// int t = in.nextInt();
//
// for (int i = 0; i < t; i++) {
// int n = in.nextInt();
// String str = "";
// for (int j = 0; j < n+1; j++) {
// String si = in.nextLine();
// str = str.concat(si);
// }
// String res = getYesOrNo(n, str);
// System.out.println(res);
// }
// }
// public static String getYesOrNo(int n, String str) {
// String res = "No";
//
// if (n == 1) {
// res = "Yes";
// return res;
// }
//
// Map<String, Integer> map = new HashMap<>();
//
// for (int i = 0; i < str.length(); i++) {
// String s = str.substring(i, i + 1);
// if (map.containsKey(s)) {
// int count = map.get(s) + 1;
// map.put(s, count);
// } else {
// map.put(s, 1);
// }
// }
//
// int[] singal = new int[1];
// singal[0] = 0;
// map.forEach(((key, value) -> {
// if (value % 2 == 1) {
// singal[0]++;
// }
// }));
//
// if (singal[0] > 1) {
// res = "No";
// return res;
// } else if (singal[0] == 1) {
// if (str.length() % 2 == 1) {
// res = "Yes";
// }
// } else {
// res = "Yes";
// }
//
// return res;
// }
}
50.dd爱科学1.0
题目:
解题思路:
- AI
通过率:100%
代码:
import java.util.Scanner;
public class Main50 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String str = in.nextLine();
int count = op(n, str);
System.out.println(count);
}
// AI搜的,100%,可通过,不理解
public static int op(int n, String str) {
int count = 0;
if (str.length() < 2) {
return 0;
}
// 用来存储不同长度的非递减子序列的最小末尾字符
int[] dp = new int[n];
int maxLength = 0;
for (int i = 0; i < n; i++) {
int left = 0, right = maxLength;
// 二分查找第一个大于 c 的元素
while (left < right) {
int mid = left + (right - left) / 2;
if (dp[mid] <= str.charAt(i)) {
left = mid + 1;
} else {
right = mid;
}
}
// 如果所有元素都 <= c,则 left = maxLength,直接追加
if (left == maxLength) {
dp[maxLength++] = str.charAt(i);
} else {
// 否则替换第一个大于 c 的元素
dp[left] = str.charAt(i);
}
}
count = str.length() - maxLength;
return count;
}
}
1688

被折叠的 条评论
为什么被折叠?



