华为机试题目菜鸟笔记C++&Java
华为机试菜鸟笔记C++&Java
1. 字符串最后一个单词的长度
#include<bits/stdc++.h>//万能头文件
using namespace std;
int main(){
string s;
while(cin >> s);
cout << s.size();
return 0;
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
- cin 输入的机制,有空格会停止,系统把空格作为数据间的分隔符,整个英文句子会一个单词一个单词的读。非常巧妙的使用了 cin 的读取逻辑。
- cin.getline()接收字符串,可以输入空格并输出。
- https://baike.baidu.com/item/cin/23384763?fr=aladdin
2.计算某字符出现次数
#include<bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s);//输入字符串
char c = tolower(getchar());//字符小写
uint16_t n = 0;
for (auto i : s) { //简单理解为 从第一个字符遍历
if (tolower(i) == c) {
++n;
}
}
cout << n << endl;
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
- tolower()是一种函数,功能是把字母字符转换成小写,非字母字符不做出处理。
- getchar()函数的作用是从计算机终端(一般为键盘)获取一个无符号字符。
typedefint int16_t; typedef unsigned int uint16_t;
- uint16_t 这些数据类型中都带有_t, _t 表示这些数据类型是通过 typedef 定义的,而不是新的数据类型。也就是说,它们其实是我们已知的类型的别名。使用的原因:方便代码的维护。
- https://blog.csdn.net/xiejingfa/article/details/50469045
3.HJ3 明明的随机数
#include<bits/stdc++.h>
int main(){
int n;
int a;
//以数组下标来存储随机数,下标对应的数组值为1,来说明是否是存储的随机数
while(~scanf("%d",&n)){//while(~scanf("%d",&n))<=> while(scanf("%d",&n)!=EOF)
int count[1001]={0};
int i;
for(i=0;i<n;i++){
scanf("%d",&a);//&:引用或者指针、
count[a]=1;
}
for(i=0;i<1001;i++){
if(count[i]==1){
printf("%d\n",i);
}
}
}
return 0;
}
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
while(~scanf("%d",&n)) <=> while(scanf("%d",&n)!=EOF)
-1 在内存中:1111 1111(八个一),原理如下:设(1111 1111)为原码,如果要想知道原码的十进制数是多少,需要做一下处理。
先判断:当最高位是 0 时,表示正数,正数的原码=反码=补码,当最高位为 1 时,表示负数,负数的原码取反为反码,然后反码加一为补码,补码就是这个负数的绝对值。
,第一步,取反;最高位为符号位,把(1111 1111)取反就为反码(0000 0000)8 个 0,
第二步,反码加一;加 1 等于(0000 0001),这儿等到的(0000 0001)就是(1111 1111)的补码,补码(0000 0001)的十进制是 1,这儿的 1 就是这个负数的绝对值。完毕。
4.HJ4 字符串分隔.
#include <bits/stdc++.h>
using namespace std;
int main()
{
string str;
while (cin >> str)
{
// 补0
int len = str.size();
if (len % 8 != 0)
{
int count = 8 - len % 8;
str.append(count, '0');//添加几个相同的字符:
}
// 按格式输出
int newLen = str.size();
for (int i = 0; i < newLen; i += 8)
{
cout << str.substr(i, 8) << endl;//从i开始输出8个长度的字符串
}
}
return 0;
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
value = atoi(dateStr.substr(i, 2).c_str());
里面包含三个函数,分别是 atoi(),substr(),c_str().
百度:
atoi()函数原型为: int atoi(char *str),用途是将字符串转换成一个整数值,str 是待转化成整数值的字符串.成功则返回转化后的整数值,失败返回 0.
substr()函数原型为:basic string::substr(string,start,length),也可把 string 移到外面,为 string &a,a.substr(start,length),其中 a 是待截取的字符串,start 表示从截取开始的前一位,length 表示截取长度,例如 string &a="hello world",则 a.substr(6,5)=world.
c_str()函数原型为:const char c_str(),如果要将 string 对象,转化为 char 对象,c_str()提供了这样一种方法,它返回一个客户程序可读不可改的指向字符数组的指针。
所以 value=atoi(dateStr.substr(i,2).c_str())的作用就是,截取 string 型的对象 dateStr,从第 i 个字符截取 2 个长度的,并转化为 char*对象,然后将此字符串转换成一个整数值,赋值给 value(value 是 int 型).
5.HJ5 进制转换
#include<bits/stdc++.h>
using namespace std;
int main() {
string str;
while (cin >> str) {
cout << stoi(str, 0, 16) << endl;
}
}
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
stoi()表示将字符串转换为整数 ,它是 C ++ STL 中的标准库函数,用于将各种格式(例如二进制,八进制,十六进制或字符串格式的简单数字)的给定字符串转换为整数。
句法:int stoi(字符串,起始位置,n 进制),将 n 进制的字符串转化为十进制
6.HJ6 质数因子
知识点
- 质数定义:质数/素数:质数又称素数。一个大于 1 的自然数,除了 1 和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定 1 既不是质数也不是合数)。
- 质数因子:质因子(或质因数)在数论里是指能整除给定正整数的质数。根据算术基本定理,不考虑排列顺序下,每个正整数都能够以唯一的方式表示成它的质因数的乘积。下面求解某一个数的质因子的情况。
C ++ 中的 sqrt(x)函数返回数字的平方根。x 为 int、double、float 类型的非负参数
双单引号区别:"字符串"、'单字符'
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
//创建扫描器对象
Scanner scanner = new Scanner(System.in);
//从键盘中接收输入
long num = scanner.nextLong();
//我们判断数 num 是不是质数时,没必要从 2 一直尝试到 num 此题中的大循环也大可不必写一个到 num 的循环,写到num的平方根即可,如果此时数字还没有除数,则可判定其本身是一个质数,没有再除下去的必要了,直接打印其本身即可
long k = (long)Math.sqrt(num);
//先求num的质数,
//注意,num一直在变小,这就暗含了一个现象或者说理论吧,就是从小到大试:如果一个数已经不能整除2了,那么这个数肯定不能整除2的倍数;同理3也一样,所以整个循环能够保证一旦出现新的因数,其一定就是质数
for (long i = 2; i <= k ; ++i) {
while (num % i == 0) {
System.out.print(i + " ");
num /= i;
}
}
System.out.println(num==1?"":num+"");
}
}
- next():next()方法将输入读取到空格字符。一旦遇到空格,它就返回字符串(不包括空格)。
- nextLine():与 next()不同,nextLine()方法读取包括空格在内的整个输入行。当该方法遇到下一行字符\n 时终止。
7.HJ7 取近似值
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
double number = in.nextDouble();
System.out.println((int)(number + 0.5));
}
}
8.HJ8 合并表记录
知识点 1:
Java Map 接口
Java HashMap
Java for-each 循环(遍历循环/增强的 for 循环)
知识点 2:
Java 基本数据类型及取值范围
解惑:
main 主函数(主方法)里头的参数 String[] args 有啥作用?
int 和 Integer 的区别
int 和 Integer 的区别主要体现在以下几个方面:
- 数据类型不同:int 是基础数据类型,而 Integer 是包装数据类型;
- 默认值不同:int 的默认值是 0,而 Integer 的默认值是 null;
- 内存中存储的方式不同:int 在内存中直接存储的是数据值,而 Integer 实际存储的是对象引用,当 new 一个 Integer 时实际上是生成一个指针指向此对象;
- 实例化方式不同:Integer 必须实例化才可以使用,而 int 不需要;包装类的存在解决了基本数据类型无法做到的事情泛型类型参数、序列化、类型转换、高频区间数据缓存等问题。
- 变量的比较方式不同:int 可以使用 == 来对比两个变量是否相等,而 Integer 一定要使用 equals 来比较两个变量是否相等。
采用 hash 表存放数据就好了,每次输入查询是否有存在的 key,存在就将 value 累加到表中,最后输出表。
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int tableSize = scanner.nextInt();
//使用HashMap类创建map,tablesize为table容量
Map<Integer, Integer> table = new HashMap<>(tableSize);
for (int i = 0; i < tableSize; i++) {
int key = scanner.nextInt();
int value = scanner.nextInt();
//containsKey(key)检查指定的键Key是否在map中
if (table.containsKey(key)) {
table.put(key, table.get(key) + value);
} else {
table.put(key, value);
}
}
//for-each循环增强的for循环,更易写和可读
for (Integer key : table.keySet()) {//返回Map集合中存在的所有键的集合
System.out.println( key + " " + table.get(key));
}
}
}
9.HJ9 提取不重复的整数
大知识点:java 集合框架之间的关系和对应联系。
知识点:
/ 在 java 中表示整除,整数和整数运算得到的一定是整数;只要有小数参与整除/得到的一定是小数。
有时间看:
区分点:
集合框架与 Collection(集合)接口:Collection 接口是集合框架的根接口。 该框架还包括其他接口:Map 和 Iterator。 这些接口也可能具有子接口。
&---Collection接口:单列集合,用来存储一个一个的对象 &---List接口:存储有序的、可重复的数据。 -->“动态”数组 (ArrayList、LinkedList、Vector) &---Set接口:存储无序的、不可重复的数据 -->高中讲的“集合” (HashSet、LinkedHashSet、TreeSet) &---Queue接口:先进先出的方式存储与访问 &---Map接口:双列集合,用来存储一对(key - value)一对的数据 -->高中函数:y = f(x) (HashMap、LinkedHashMap、TreeMap、Hashtable、Properties) Java Iterator 接口 在Java中,Iterator接口提供了用于访问集合元素的方法。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) {
// 使用HashSet来判断是否是不重复的
//当执行add添加后会返回一个 boolean,如过插入成功则放回true反正则为false
HashSet<Integer> hs = new HashSet<>();//默认容量将为 16,负载因子将为 0.75
int target = sc.nextInt();//Scanner的方法
while (target != 0) {
int temp=target%10;
if(hs.add(temp))
System.out.print(temp);
// 除10能去掉最右边的数字,/ 在java中表示整除,整数和整数运算得到的一定是整数;只要有小数参与整除/得到的一定是小数。
target/=10;
}
System.out.println();
}
}
}
知识点:
[StringBuffur]:见 11 题
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//逆序排列,StringBuilder(String str),并调用StringBuilder的reverse()将字符串翻转。
StringBuilder stringBuilder = new StringBuilder(scanner.nextLine()).reverse();
String str = stringBuilder.toString();//j转换为String对象。
//结果集
StringBuilder result = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
//indexOf() 返回字符串中指定字符/子字符串首次出现的索引。
//charAt() 返回给定索引处的字符
if (str.indexOf(str.charAt(i)) == i) {
//append(obj)添加任意类的字符串,并返回当前对象(StringBuilder)
result.append(str.charAt(i));
}
}
System.out.println(result.toString());
}
}
10.HJ10 字符个数统计
知识点:
Java BitSet 类:Bitset 类创建一种特殊类型的数组来保存位值。BitSet 中数组大小会随需要增加。
BitSet 就是位图,它的值只有 1 和 0。内部是基于 long[]实现的,long 是 8 字节(64 位),所以 Bitset 最小是 64 位,每次扩大一次扩大 64 位,即内部大小是 64 的倍数。每次 BitSet 新增加一个数字时,就将该位置为 1。也就是说 BitSet 并不直接存储每个数据,而是存储数字是否存在过(1 表示存在,0 表示不存在)。
字节(byte)、位/比特(bite)、字符之间的关系。
bit
比特/位:二进制的每一个 0/1 位,计算机内部数据储存的最小单位。
byte
字节:字节是计算机中最小的存储单元
计算机中 数据处理 的基本单位,任何数据都是以字节
的方式存储。
一个byte
(字节) = 8 个bit
(位)即 0000-0000,8 个二进制位。
char
字符 :占用 2 个 byte(字节)Java--位、字节、字符、字符编码、数据存储单位 & 电脑的 32 位和 64 位的区别是什么呢
大小关系:bit(位/比特) < byte(字节) < kb(千字节) < MB(兆字节) < GB(吉字节) < TB(太字节) <.....
A?B:C
其实为 Java 中的三元运算符,表示条件判断语句,对布尔类型的语句进行判断,即 A ? B : C,表示:如果语句 A 为真,则执行语句 B,如果语句 A 为假,则执行语句 C。
凡是涉及到去重统计都可以用位图实现。因为每一个不同的数据只需要用二进制的一位存储即可,大大减小了统计所使用的存储空间
import java.util.Scanner;
import java.util.BitSet;
public class Main {
public static void main(String[] args) {
Scanner scannner = new Scanner(System.in);
String str=scannner.next();
//总共有128个字符。字需要用128位
BitSet bitSet=new BitSet(128);//BitSet位图
for(char c:str.toCharArray()){//toCharArray():将字符串转换为char数组
//判断字符c是否已出现
if(!bitSet.get(c)){/*boolean get(int index)
返回指定索引处的位值。自动类型转换。。。而且转成的int恰好就是该字符对应的ASCII码*/
//未出现就设置为已出现
bitSet.set(c);//void set(int index):将指定索引处的位设置为 true。
}
}
//统计有多少字符出现过
System.out.println(bitSet.cardinality());//int cardinality( ):返回此 BitSet 中设置为 true 的位数。
}
}
11.HJ11 数字颠倒#重要知识点#
知识点:
StringBuilder 和 StringBuffer 是一对兄弟,因为它们拥有同一个父类 AbstractStringBuilder,同时实现的接口也是完全一样,都实现了 java.io.Serializable, CharSequence 两个接口。
那它们有什么区别呢?最大的区别在于 StringBuffer 对几乎所有的方法都实现了同步,StringBuilder 没有实现同步,如同样是对 AbstractStringBuilder 方法 append 的重写,StringBuffer 添加了 synchronized 关键字修饰,而 StringBuilder 没有。
所以 StringBuffer 是线程安全的,在多线程系统中可以保证数据同步,而 StringBuilder 无法保证线程安全,所以多线程系统中不能使用 StringBuilder。
但是方法同步需要消耗一定的系统资源,所以 StringBuffer 虽然安全,但是效率不如 StringBuilder,也就是说使用 StringBuilder 更快。
String、StringBuffer、StringBuilder 有什么区别?
1、String 一旦创建不可变,如果修改即创建新的对象,StringBuffer 和 StringBuilder 可变,修改之后引用不变。
2、String 对象直接拼接效率高,但是如果执行的是间接拼接,效率很低,而 StringBuffer 和 StringBuilder 的效率更高,同时 StringBuilder 的效率高于 StringBuffer。
3、StringBuffer 的方法是线程安全的,StringBuilder 是线程不安全的,在考虑线程安全的情况下,应该使用 StringBuffer。
next() 和 nextLine()的区别``(可以输入数值型数据,返回值为 String)
-
String next()
:next()方法不能得到带有空格的字符串。一旦遇到空格,它就返回字符串(不包括空格)。-
String nextLine()
:与 next()不同,nextLine()方法读取包括空格在内的整个输入行。当该方法遇到下一行字符\n 时终止。hasNext() 和 hasNextLine() 的区别``(返回值为 Boolean)
-
boolean hasNext()
:断接下来是否有非空字符.如果有,则返回 true。-
boolean hasNextLine()
:根据行匹配模式去判断接下来是否有一行(包括空行),如果有,则返回 true。总结:采用 hasNextXxxx() 的话,后面也要用 nextXxxx()
如果要输入 int 或 float 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取:
采用 hasNextXxxx() 的话,后面也要用 nextXxxx(): 比如前面用 hasNextLine(),那么后面要用 nextLine() 来处理输入; 后面用 nextInt() 方法的话,那么前面要使用 hasNext()方法去判断.
附上 java 如何在输入空时让 scanner 类自动结束
public static void main(String[] args){ Scanner sc = new Scanner(System.in); while (sc.hasNextLine()){ String str = sc.nextLine(); if(str.isEmpty()){ break; } System.out.println(str); } }
菜鸟随记:
- 使用一下 java 中的注释标签
/**
* @author PickUpShells
* @version 1.0.0
* @ClassName HW11.java
* @Description 数字颠倒
* @createTime 2022年01月19日 11:51:00
*/
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
String str =in.nextLine();//可以输入int类型,会被转换为字符串,以/n(回车)未结束符
StringBuffer strb=new StringBuffer(str);//StringBuffer比StringBuilder略慢但是线程安全
strb.reverse();//字符串顺序翻转
System.out.println(strb.toString());
}
}
12.HJ12 字符串反转
知识点:
System.out.print 和 System.out.println 区别
参数有区别:
System.out.println() 可以不写参数
System.out.print(参数) 参数不能为空.必须有
效果有区别
println :会在输出完信息后进行换行,产生一个新行
print: 不会产生新行
println 更简洁, print 更灵活
print 可以后面跟"\n"来达到和 println 一样的效果也可以跟"\t" 制表符, 等.
对象定义中的 <>
相关:java 泛型
Stack<Type> stacks = new Stack<>(); //<Type>数据类型,代表该Stack中只能放入Type类或者其子类的实例。 //? 第二个<>也许指的是大小
import java.util.ArrayList; // 引入 ArrayList 类 ArrayList<E> objectName =new ArrayList<>(); // 初始化 E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。 objectName: 对象名。 ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
菜鸟随记:改版了,更换为有序列表
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Stack stack=new Stack();
String str=in.nextLine();
for(int i=0; i< str.trim().length();i++){//trim():删除任何前导(开始)和尾随(结束)空格,这里没有也可以
stack.push(str.charAt(i));///push():将元素添加到堆栈的顶部
}
while(!stack.empty()){//empty():检查堆栈是否为空
System.out.print(stack.pop());//pop():从堆栈顶部删除元素,并返回该元素
}
}
}
13.HJ13 句子逆序
知识点:
Java String split()方法在指定的正则表达式处分割字符串,并返回子字符串数组。
string.split(String regex, int limit)
- regex - 字符串在此正则表达式处分割(可以是字符串)
- limit (可选)-指定生成的子字符串的数量
如果未传递参数 limit,则 split()返回所有可能的子字符串。
菜鸟随记:初次接触正则表达式,之前也听说,见过但是他认识我我不认识他了。ε=(´ο`*)))唉
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str=in.nextLine();
String s[]=str.split(" ");//split():将字符串拆分为指定的字符串(正则表达式)
for(int i=s.length-1;i>=0;i--){
if(i!=0)
System.out.print(s[i]+" ");
else
System.out.print(s[i]);
}
}
}
14.HJ14 字符串排序 #未完成#
知识点:
- IOException :IOException 也称为检查异常。它们由编译器在编译时检查,并提示程序员处理这些异常。
- RuntimeException:一个运行时异常发生由于编程错误。它们也称为非检查异常。这些异常不在编译时检查,而是在运行时检查。
一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。下图是一个描述输入流和输出流的类层次图。
BufferedReader InputStreamReader
- br.readLine():read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
- br.close():关闭缓冲的读取器,调用 close()方法后,我们将无法使用读取器读取数据。
InputStreamReader.ready()
通知此流是否已准备好被读取。
如果下一读取保证不会阻塞输入,该方法返回 true,否则返回 false。
将字符串参数作为有符号的十进制整数进行解析。
如果方法有两个参数, 使用第二个参数指定的基数(进制数),将字符串参数解析为有符号的 10 进制整数。
String[] ss = new String[n]
前半句为声明——Java 是不分配内存的,
后半句为初始化——初始化必须要让编译器知道大小,只有创建的时候也就是 new 的时候才会分配内存。
Arrays.stream(ss).sorted().forEach(System.out::println);
Java 8 新特性-部分
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
Java8 推出了属于 Java 的 lambda 表达式,与一众的 => 不同,Java 选择了 -> 做为箭头符号。
ambda表达式的基本格式:()->{}
- 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream 提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
- Collectors.joining()
- Java 8 Stream.distinct() 列表去重示例 #
- java 中 Random 的使用
Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了 10 个随机数:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
forEach() 方法与 for-each 循环不同。Java for-each 用于遍历数组中的每个元素。
例子:
Arrays.stream(ss).sorted().forEach(System.out::println); list.forEach(System.out::println);
Java.Util.Stream //?看到第 13 个
.forEach(System.out::println);
- ::是什么,怎么使用
pq.offer(s);
菜鸟随记:这个解法完全超出我知识范围了,呜呜呜。知识点太多了,看了一遍好像啥也没记住 o(╥﹏╥)o,尤其是这个 lambda 表达式和.forEach()
时隔一周之后来解决问题:发现这是 Java8 的新特性,之前完全没接触过……
第二天继续看相关知识点
import java.util.*;
import java.io.*;
public class Main {
// API中的sort方法大多使用快排或者归并排序;
// stream可以更方便写代码,但在不同数据量但情况下效率不同,姑且可以认为更耗时
//主函数调用实现方法解决问题
public static void main(String[] args) throws IOException {
withArraysAPI();
//withComparator();
//withPriorityQueue();
}
//方法1:调用API实现
public static void withArraysAPI() throws IOException {
//从输入流中读取并存储数组中的字符串
//读取控制台输入:Java 的控制台输入由 System.in 完成。为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//Integer.parseInt br.readLine()
int n = Integer.parseInt(br.readLine());
String[] ss = new String[n]; // n是大小但是……请仔细读题
for (int i = 0; i < n; ++i) {
ss[i] = br.readLine();
}
br.close();
// use stream to sort and output strings, which may use more time
Arrays.stream(ss).sorted().forEach(System.out::println);//?
// 或许, 使用 Arrays.sort(Object[] a) and for loop to output, which may use less time
}
//方法二: 使用PriorityQueue
public static void withPriorityQueue()throws IOException {
// read and store strings in a priority queue from input stream
PriorityQueue<String> pq = new PriorityQueue<>();
//BufferedReader InputStreamReader(System.in)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();//ReadLine:read the first line, but the number will not be used.
String s;
while ((s = br.readLine()) != null) {
pq.offer(s);//pq.offer(s)
}
br.close();
//输出
while (!pq.isEmpty()) {
System.out.println(pq.poll());//pq.poll()
}
}
//方法三: 使用list并自己实现Comparator
public static void withComparator() throws IOException {
// read and store strings in a list from input stream
List <String> list = new ArrayList<>();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();// 读取第一行,但将不使用该数字。
String s;
while ((s = br.readLine()) != null) {
list.add(s);
}
br.close();
//sort with self defined comparator
list.sort((s1, s2) -> {
int i = 0;
while (i < s1.length() && i < s2.length()) {
if (s1.charAt(i) > s2.charAt(i)) {
return 1;
} else if (s1.charAt(i) < s2.charAt(i)) {
return -1;
} else {
i++;
}
}
return s1.length() - s2.length();
});
// indeed, default comparator works for this case
// list.sort(null);
// or you may use Collections.sort method to avoid null
// Collections.sort(list);
//输出
list.forEach(System.out::println);//?::
}
}
15.HJ15 求 int 型正整数在内存中存储时 1 的个数
知识点:
num&1:num 与 1 做按位与,可以判断 num 的奇偶性。
按位与结果取决于 num 最后一位,如果 num 最后一位是 1,则结果为 1,反之结果为 0。
if( (n&1)==1 ) 判断 num 最后 1 位是否为 1,可以用来判断奇偶性。
- 举例:num=3 的话
3 与 1 的与运算就是(先写成 2 进制,然后同位比较,都为 1 时此位为 1,否则为 0):
11 & 01 = 01
因此 3 与 1 的与运算就是 1- 拓展:位运算——按位与(&)、按位或(|)、按位异或(^)
按位与(&)
两个数进行按位与运算时,先将其分别换算成二进制数再进行运算,按位与简单的理解就是同位上的两个数只有同为真时则真,一假则假,1 为真,0 为假
示例:17&23 结果为十进制就是 17
0000 0000 0001 0001(17)
0000 0000 0001 0111(23)
——————————
0000 0000 0001 0001(17)按位或(|)
同上方式,只是要求是一真为真,同假才假,只要有一个为 1 则为 1,只有都是 0 的时候才为 0,同样用刚才的例子 17 与 23 进行按位或运算
示例:17|23=23
0000 0000 0001 0001(17)
0000 0000 0001 0111(23)
——————————
0000 0000 0001 0111(23)取反(~)
每个位上都取相反值,1 变成 0,0 变成 1。
0000 0000 0001 0001(17)
1111 1111 1110 1110(~17)
所以:~17 =1111 1111 1110 1110
按位异或(^)
同为假,异为真,还是 17 和 23
0000 0000 0001 0001
0000 0000 0001 0111
——————————
0000 0000 0000 0110
所以:17^23=0000 0000 0000 0110
总结:任何数与 0 异或,结果都是其本身。利用异或可以实现交换算法,例:左移(<<)
将一个数各二进制位全部向左移动若干位。
示例:17<< 2 =0000 0000 0100 0100 =68
左移一位的结果就是原值乘 2,左移两位的结果就是原值乘 4。
右移(>>)
将一个数各二进制位全部向右移动若干位。
示例:17 >> 2 =0000 0000 0000 0100 = 4
右移一位的结果就是原值除 2,右移两位的结果就是原值除 4,除了以后没有小数位的,都是取整。
菜鸟随记:
比较简单,缓解一下我焦虑的心情:真不错啊、真不错...
先把十进制数字转成二进制数字的字符串,再将字符串中的“0”全部替换为空,字符串的长度就是 1 的个数。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int num = in.nextInt();
String str = Integer.toBinaryString(num);
String newStr = str.replaceAll("0", "");
System.out.println(newStr.length());
}
}
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
int num = in.nextInt(); //读取数字
int n = 0; //计数变量
for(int i=0; i < 32; i++){
if((num&1) == 1) //如果末位为1则计数
n++;
num = num >>> 1; //无符号右移
}
System.out.println(n);
}
}
知识点:
-
菜鸟随记:
知识点:
-
菜鸟随记:
16.HJ16 购物单#背包问题#
知识点:
0\1 背包问题:听懂不翻车系列之--背包问//? 待完成
第一节课:0/1 背包问题
基本型:归纳得出,存储了所有的决策结果
完全型:由上推下,滚动数组 1 行(空间压缩:优化空间复杂度)
第二节课:
菜鸟随记:遇到的第一个难题,之前都不算难,呜呜呜。
本题为变形的 01 背包:题目提示
物品数组:
物品\属性 0是价格
1是重要度
2是主附件
3是附件1
4是附件2
…… i物品编号
…… 背包数组:money+1 是元素个数==数组下标 money
j剩余的钱
价值*重要度
money+1 …… …… 0 几种选择情况
一. 只买主件
二. 买主件和第一个附件
三. 买主件和第二个附件
四. 买主件和两个附件
五. 什么都不买
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int money = sc.nextInt();//有多少钱
int num = sc.nextInt();//物品个数
//0是价格,1是重要度,2是主附件,3是附件1,4是附件2.
int[][] goods = new int[num + 1][5];
for (int i = 1; i <= num; i++) {
int v = sc.nextInt();
int p = sc.nextInt();
int q = sc.nextInt();
goods[i][0] = v;//价格
goods[i][1] = p;//重要度
goods[i][2] = q;//主附件
if (q != 0) { //如果是附件,往对应主件的3或4里面填该附件的序号
if (goods[q][3] == 0) { //如果已经是附件1了则标记为附件2
goods[q][3] = i;
} else {
goods[q][4] = i;
}
}
}
/*
for (int i = 0; i <= m; i++) {
System.out.println(Arrays.toString(goods[i]));//这里可看下物品数组
}
*/
int[]vw = new int[money + 1]; //dp数组的简化版(结合视频理解)
for (int i = 1; i <= num; i++) {
if (goods[i][2] == 0) { //如果是主件
//01背包用倒序(结合视频理解)
for (int j = money; j >= goods[i][0]; j--) {
//单主件;vw[j-goods[i][0]]:不拿上一个物品的满意度
vw[j] = Math.max(vw[j], vw[j - goods[i][0]] + goods[i][0] * goods[i][1]);
//有附件1:
if (goods[i][3] != 0 && j >= goods[i][0] + goods[goods[i][3]][0]) {
vw[j] = Math.max(vw[j], vw[j - goods[i][0] - goods[goods[i][3]][0]] +
goods[i][0] * goods[i][1] + goods[goods[i][3]][0] * goods[goods[i][3]][1]);
}
//有附件2
if (goods[i][3] != 0 && j >= goods[i][0] + goods[goods[i][4]][0]) {
vw[j] = Math.max(vw[j], vw[j - goods[i][0] - goods[goods[i][4]][0]] +
goods[i][0] * goods[i][1] + goods[goods[i][4]][0] * goods[goods[i][4]][1]);
}
//有附件1和附件2:
if (goods[i][4] != 0 && j >= goods[i][0] + goods[goods[i][3]][0] + goods[goods[i][4]][0]) {
vw[j] = Math.max(vw[j], vw[j - goods[i][0] - goods[goods[i][3]][0] - goods[goods[i][4]][0]] +
goods[i][0] * goods[i][1] + goods[goods[i][3]][0] * goods[goods[i][3]][1] +
goods[goods[i][4]][0] * goods[goods[i][4]][1]);
}
}
}
//System.out.println(Arrays.toString(vw));//这里可看每一行dp数组
}
System.out.println(vw[money]); //dp数组最后一位即答案
}
}
HJ17 坐标移动
知识点:
正则表达式-菜鸟教程//? 没看呢
Java 中的 continue 语句跳过循环的当前迭代(for,while,do…while 等),程序的控制权移到循环的末尾。
continue 语句几乎总是在(if ... else 语句)决策语句中使用。它的语法是:
continue;
- Java 中的 break 语句立即终止循环,程序的控制权移至循环后的下一条语句。
-
String 的方法
String.substring():返回给定字符串的子字符串
string.substring(int startIndex, int endIndex)//下标从0开始
String.charAt():返回给定索引处的字符
String.split():将字符串拆分为指定的字符串(正则表达式)
valueOf() 方法用于返回给定参数的原生 Number 对象值,参数可以是原生数据类型, String 等。
Integer valueOf(String s):返回保存指定的 String 的值的 Integer 对象。
该方法是静态方法。该方法可以接收两个参数一个是字符串,一个是基数。
菜鸟随记:第二次碰到正则表达式了,
复杂度分析
时间复杂度:O(n)O(n)O(n),nnn 为输入的字符串长度,不管是正则表达式的匹配还是计算数字,总体上最多遍历字符串每个字符
空间复杂度:O(1)O(1)O(1),正则表达式空间为常数
import java.util.*;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader (new InputStreamReader(System.in));
String[] in = bf.readLine().split(";");
int x = 0;
int y = 0;
for (String s : in) {
//不满足题目给定坐标规则
//合法字符就是ASDW中一个字母后面接上1位或者2位数字,正则表达式为"[WASD][0-9]{1,2}"
if (!s.matches("[WASD][0-9]{1,2}")) { //[]:匹配包含的任一字符;{}:匹配至少 n 次,至多 m 次
continue;//continue:跳到下一次循环;break:跳出当前所在循环
}
int val = Integer.valueOf(s.substring(1)); //valueOf()将参数转换为Integer类型;String.substring()返回给定字符串从指定下标开始的子字符串
switch (s.charAt(0)) { //String.charAt():返回给定索引处的字符
case 'W':
y += val;
break;
case 'S':
y -= val;
break;
case 'A':
x -= val;
break;
case 'D':
x += val;
break;
}
}
System.out.println(x + "," + y);
}
}
18.HJ18 识别有效的 IP 地址和掩码并进行分类统
#IP与子网掩码#
#难#
知识点:
Java Number & Math 类:Boolean 和 boolean 区别
Boolean 类型的值不是只有 true 和 false 两种吗?为什么他定义的属性出现了 null 值?
我们应该先明确一点,boolean 是 Java 的基本数据类型,Boolean 是 Java 的一个类。boolean 类型会在“赋零值”阶段给属性赋 false。而 Boolean 是一个类,会在“赋零值”阶段给对象赋 null。
多个 if……continue;
每一个都相当与跳出并进入下一次循环
字符串 1+ 字符串 2:直接在 1 其后追加 2
!maskBinary.matches("[1]{1,}[0]{1,}")
matches() 方法用于检测字符串是否匹配给定的正则表达式。
public boolean matches(String regex)
将一个整数类型参数转换为二进制整数的字符串类型
菜鸟随记:犯了个低级错误,sc.hasNextLine()中的 L 写成小写了。改错误改了费了点时间,第二天才发现错在这里,淦~
菜鸟提示:
这里有个大坑,若是 ip 是 0 或者 127 开头的直接忽略,即使子网掩码是非法的,也不能将此算到 error 里去。
首先以行为单位接收输入的 ip 和子网掩码
其次用~分割每一行 分成 ip 和子网掩码
ip 用“.”分割,这里有个小坑,“.”是 split 的一个关键字,必须转译一下才可以使用。split(".")
分割后按照条件判断就好了
大小坑
坑一 统计错误 IP 地址或错误掩码的数目
一开始我想的是,统计每一个错误的 ip 地址和错误掩码,举个例子,0.0.10.1~255.245.55 ,ip 是错的,子网掩码也是错的,所以算俩个,后面才发现,ip 和子网掩码都错只算一个。
坑二 私有地址和公有地址是不冲突的,都算
坑三 判断子网掩码错误时,忽略了全是 1 和全是 0 的情况
坑四 2 进制的方式判断子网掩码时忽略了转化后的二进制码值没有自动补齐为 8 位,导致判断有 bug
举个例子<span> </span>255.16.0.0 255对应的是11111111 16对应的是100000 俩个直接拼接 变成11111111100000
但实际情况应该是<span> </span>11111111 00100000 ,转化二进制的时候应该8位补齐。
判断条件
查看 ip 第一段是否为“0”或“127”,若是忽略;
判断子网掩码是否合法,如果满足下列条件之一即为非法掩码
- 数字段数不为 4
- 在二进制下,不满足前面连续是 1,然后全是 0
- 在二进制下,全为 0 或全为 1
判断 IP 地址是否合法,如果满足下列条件之一即为非法地址
- 数字段数不为 4,比如存在空段,即【192..1.0】这种;
- 某个段的数字大于 255
判断 ip 是否是 ABCDE 类地址,若是对应类加一。 所有的 IP 地址划分为 A,B,C,D,E 五类: A 类地址 1.0.0.0 ~ 126.255.255.255; B 类地址 128.0.0.0 ~ 191.255.255.255; C 类地址 192.0.0.0 ~ 223.255.255.255; D 类地址 224.0.0.0 ~ 239.255.255.255; E 类地址 240.0.0.0 ~ 255.255.255.255
判断 ip 是否私有地址。 私网 IP 范围是: 10.0.0.0~10.255.255.255 172.16.0.0~172.31.255.255 192.168.0.0~192.168.255.255
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int aNum = 0;
int bNum = 0;
int cNum = 0;
int dNum = 0;
int eNum = 0;
int errNum = 0;
int prvNum = 0;
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextLine()) { //
String str = sc.nextLine();
String[] strArr = str.split("~");//strArr中有2个元素:[ip],[掩码]
int ipFirst = getIpSeg(strArr[0], 0); //自定义函数1:提取ip的第index个数字的值
if (ipFirst == 0 || ipFirst == 127) {
continue;
}
if (maskIsInvalid(strArr[1])) { //自定义函数2:判断掩码是否非法
errNum++;
continue;
}
if (ipIsInvalid(strArr[0])) {//自定义函数3:判断ip是否非法
errNum++;
continue;
}
if (ipFirst >= 1 && ipFirst <= 126) {
aNum++;
}
if (ipFirst >= 128 && ipFirst <= 191) {
bNum++;
}
if (ipFirst >= 192 && ipFirst <= 223) {
cNum++;
}
if (ipFirst >= 224 && ipFirst <= 239) {
dNum++;
}
if (ipFirst >= 240 && ipFirst <= 255) {
eNum++;
}
int ipSecond = getIpSeg(strArr[0], 1);
if (ipFirst == 10 || (ipFirst == 172 && ipSecond >= 16 && ipSecond <= 31) ||
(ipFirst == 192 && ipSecond == 168)) {
prvNum++;
}
}
System.out.println(aNum + " " + bNum + " " + cNum + " " + dNum + " " + eNum +
" " + errNum + " " + prvNum);
}
//判断掩码是否非法
public static boolean maskIsInvalid(String mask) {
//“.”是split的一个关键字,必须转译一下才可以使用。split("\\.")
String[] maskArr = mask.split("\\.");
if (maskArr.length != 4) { //数字段数不为4
return true;
}
String maskBinary = toBinary(maskArr[0]) + toBinary(maskArr[1]) + toBinary(
maskArr[2]) + toBinary(maskArr[3]); //字符串1+字符串2:直接在1其后追加2
//正则表达式:[]:匹配包含的任一字符;{}:n 次匹配至少 n 次,至多 m 次
if (!maskBinary.matches("[1]{1,}[0]{1,}")) { //matches() 方法用于检测字符串是否匹配给定的正则表达式。
return true;
}
return false;
}
//将String num转换为String 二进制01
public static String toBinary(String num) {
//Integer.toBinaryString();Integer.valueOf(num)
String numBinary = Integer.toBinaryString(Integer.valueOf(num));
while (numBinary.length() < 8) { //2^8=256
numBinary = "0" + numBinary;//如果num=0,那么生成8位2进制字符“0”
}
return numBinary;
}
//Boolean和boolean
public static boolean ipIsInvalid(String ip) {
String[] ipArr = ip.split("\\.");
if (ipArr.length !=4) { //比如存在空段,即【192..1.0】这种;length是指有多少个元素
return true;
}
//某个段的数字大于255为非法
if (Integer.valueOf(ipArr[0]) > 255 || Integer.valueOf(ipArr[1]) > 255 ||
Integer.valueOf(ipArr[2]) > 255 || Integer.valueOf(ipArr[3]) > 255) {
return true;
}
return false;
}
//提取ip的第index个数字的值
public static int getIpSeg(String ip, int index) {
String[] ipArr = ip.split("\\.");//ipArr中有4个字符串数字元素
return Integer.valueOf(ipArr[index]);
}
}
19.HJ19 简单错误记录
知识点:
使用 IO 流必须抛出 IOException 吗
好像是的
Java String split()方法在指定的正则表达式处分割字符串,并返回子字符串数组。
string.split(String regex, int limit)
regex - 字符串在此正则表达式处分割(可以是字符串)
limit (可选)-指定生成的子字符串的数量
如果未传递参数 limit,则 split()返回所有可能的子字符串。
HashMap<String, Integer> map = new LinkedHashMap<String, Integer>();
LinkedHashMap 继承于 HashMap,使用元素的自然顺序对元素进行排序.
<String, Integer> 用于指定 <Key,Value> 的包装类/数据类型
创建一个 HashSet 对象 sites,用于保存字符串元素:
HashSet<String> sites = new HashSet<String>();
菜鸟随记:忘记抛出 IOException 了,感觉好像是必须的啊?一直没理清 <> 中数据类型的含义这下有点清楚了。
import java.util.*;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
HashMap<String, Integer> map = new LinkedHashMap<String, Integer>(); //LinkedHashMap继承于HashMap,使用元素的自然顺序对元素进行排序.
String textStr = null;
// 注意 hasNext 和 hasNextLine 的区别
while ((textStr = bf.readLine()) !=null) { //&& !textStr.equals(""))没有性能影响
String[] str =textStr.split("\\s+"); //textStr.split("\\s+"):可以接regex,\s+ 可以匹配多个空格
String fileName = str[0].substring(str[0].lastIndexOf("\\") +1); //str[0].lastIndexOf():lastIndexOf("\\") 取的是“\"在字符串中最后一次出现的下标,再+1取的是文件名开始的下标。substring(x),取的是从下标x开始到字符串结束的值。所以这是取文件名。
String key = fileName.substring(Math.max(fileName.length() - 16,0)) + " " + str[1];
Integer value=map.get(key);
if(value==null)
map.put(key,1);
else
map.put(key,value+1);
}
int count=0;
for(Map.Entry<String,Integer> it:map.entrySet()){
if(map.size()-count<=8)//hashmap.size()计算 HashMap 中的元素数量
System.out.println(it.getKey()+" "+it.getValue());
count++;
}
}
}
20.HJ20 密码验证合格程序#重要知识点#
知识点:
Scanner 类的 next()方法与 hasnext()方法最详细的用法解释
sc.nextLine()
- next():next()方法将输入读取到空格字符。一旦遇到空格,它就返回字符串(不包括空格)。
- nextLine():与 next()不同,nextLine()方法读取包括空格在内的整个输入行。当该方法遇到下一行字符\n 时终止。
调用 sc.next()方法时如果当前 scanner 对象的迭代器(iterator)指针指向的下一个字符串值为空(不是判断当前指针指向的值,而是判断下一个值是否为空)则将指针下移并且阻塞此方法等待用户输入,用户输入完则返回用户输入的第一个字符串(以空格为分隔符)。(scanner 对象使用 char[]数组保存用户输入的字符串,以空格作为分隔符)。
如果当前 scanner 对象的迭代器(iterator)指针指向的下一个字符串值不为空则将指针下移,然后返回当前指针指向的字符串,不会等待用户输入。sc.hasNext()是判断是否输入了内容,sc.next()取输入的字符串,sc.nextInt()取输入的整数
hasnext()方法永远不会返回 false,因为如果 scanner 对象的迭代器(iterator)指针的下一个字符串值为空时会阻塞此方法继续等待用户输入。
substring()方法从给定的字符串返回一个子字符串。-前包后不包
- 子字符串与 startIndex 中的字符一起存在,并扩展到索引 endIndex - 1 中的字符。
- 如果未传递 endIndex,则子字符串与指定索引处的字符一起存在,并扩展到字符串的末尾。
菜鸟随记:可能是刷题太少的事,感觉题干太绕了,有点模糊
理解题干“不能有长度大于 2 的不含公共元素的子串重复”:
12121ab 中,第一个‘121’跟第二个‘121’重复且长度大于 2,但是不能含公共元素第 2 个‘1’,所以是合法的,不含公共元素的意义就是比对重复的时候,别的字符串中一样的那个元素不是自己原本的就算过的元素
循环条件以及数组字符串长度取第几位理解还不到位,总感觉没理解清楚。
length-2刚好循环到倒数第三位
import java.util.*;
import java.util.regex.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) {
String password=sc.next();//? sc.next()
if(password.length()<=8){
System.out.println("NG");
continue;
}
if(!isMatchRegex(password)){
System.out.println("NG");
continue;
}
if(isRepeat(password,0,3)){
System.out.println("NG");
continue;
}
System.out.println("OK");
}
}
/*校验是否有重复子串2
private static boolean reStr(String s) {
for (int i = 3; i < s.length(); i++) {
if (s.substring(i).contains(s.substring(i - 3, i))) {
return false;
}
}
return true;
} */
//校验是否有重复子串1
private static boolean isRepeat(String pd,int headIndex,int tailIndex){
if(tailIndex>=pd.length()){
return false;
}
if(pd.substring(tailIndex).contains(pd.substring(headIndex,tailIndex))){
return true;
}else{
return isRepeat(pd,headIndex+1,tailIndex+1);
}
}
//检查是否满足正则
private static boolean isMatchRegex(String pd){
int count=0;
Pattern p1=Pattern.compile("[A-Z]");
if(p1.matcher(pd).find()){
count++;
}
Pattern p2=Pattern.compile("[a-z]");
if(p2.matcher(pd).find()){
count++;
}
Pattern p3=Pattern.compile("[0-9]");
if(p3.matcher(pd).find()){
count++;
}
Pattern p4=Pattern.compile("[^a-zA-Z0-9]");
if(p4.matcher(pd).find()){
count++;
}
if(count>=3){
return true;
}else{
return false;
}
}
}
21.HJ21 简单密码
知识点:
- Set 可以被认为是一个集合,集合内部是同类型的元素,他们之间没有先后顺序,但是不允许重复!!!
- TreeSet:元素唯一,基于平衡二叉搜索树(红黑树)实现的 Set,因为二叉搜索树中序遍历为有序的。
- HashSet 是基于哈希表实现的,因为哈希表内部元素是无序的,所以 HashSet 不像 TreeSet,HashSet 没有和元素顺序有关的方法,像上面提到的 first(),last(),lower()等。
- LinkedHashSet:直接父类是 HashSet,特点:存取有序,存储的元素不能重复。
菜鸟随记:整个题目比较简单,其中重点应该是 Set 集合和 ASCII 码。没啥理解难度
import java.util.*;
public class Main {
//定义map容器存储按键对应数字字符的容器
private static Map<String, String> map = new HashMap<>();
//静态初始化、加载map容器
static {
map.put("1", "1");
map.put("abc", "2");
map.put("def", "3");
map.put("ghi", "4");
map.put("jkl", "5");
map.put("mno", "6");
map.put("pqrs", "7");
map.put("tuv", "8");
map.put("wxyz", "9");
map.put("0", "0");
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String str = sc.nextLine();
char[] chars = str.toCharArray();
StringBuffer buffer = new StringBuffer();
for (char c : chars) {
if (c >= '0' && c <= '9') {
buffer.append(String.valueOf(c));//复习:buffer.append
} else if (c >= 'A' &&
c <= 'Y') { //如果是A~Y的大写字母则需要将其+32位转换成小写再向后移1位
char newChar = (char)(c + 32 + 1); // c+32+1 ASK吗中间隔32
buffer.append(String.valueOf(newChar));
} else if (c == 'Z') { //如果是Z则加密成a
buffer.append("a");//字符串:""/字符:''都能通过
} else {
//取出map容器中的key与字符进行校验并加密
Set<String> keys = map.keySet(); //集合(Set)内部是同类型的元素,他们之间没有先后顺序,但是不允许重复!!
for (String k : keys) {
if (k.contains(String.valueOf(c)))
buffer.append(map.get(k));// map.get(k):取k对应的value;buffer.append('c'):放入buffer中
}
}
}
System.out.println(buffer.toString());
}
}
}
22.HJ22 汽水瓶
知识点:
菜鸟随记:这个题简单,我一看题解就把自己给看乐了。
想要换最多的汽水,就要厚脸皮,每两个空瓶向老板借一瓶汽水,喝完之后拿三个空瓶再换一瓶还给老板;相当于自己每两个空瓶可以换到一瓶汽水
核心代码 bottle/2
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
int bottle = sc.nextInt();
if(bottle==0){
break;
}
System.out.println(bottle/2);
}
}
}
HJ23 删除字符串中出现次数最少的字符
知识点:
Collection 集合按照其存储结构可以分为两大类,即单列集合 Collection 和双列集合 Map。
- Collection
Collection 是单列集合的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是 List 和 Set。其中,List 的特点是元素有序,可重复。Set 的特点是元素无序,而且不可重复。List 接口的主要实现类有 ArrayList 和 LinkedList,set 接口的主要实现类有 HashSet 和 TreeSet。- Map
Map 是双列集合类的根接口,用于存储具有键(key)、值(value)映射关系的元素,每个元素都包含一对键值,在使用 Map 集合时可以通过指定的 key 找到对应的 value,Map 接口的主要实现类有 HashMap 和 TreeMap。Collections 是针对集合类的一个包装类,它提供一系列静态方法以实现对各种集合的搜索、排序、线程安全化等操作,其中大多数方法都是用来处理线性表。
Collections 类不能实例化,如同一个工具类,服务于 Collection 框架。若在使用 Collection 类的方法时,对应的 Collection 的对象为 null,则这些方法都会抛出 NullPointerException。
map.getOrDefault(c,0)
@Override public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; } //如果map中已经存在,并且已经有value值,那么就直接返回他的value值,否则设置为默认值。
str.replaceAll(String.valueOf(c), "");
Java String replaceAll()方法用指定的文本替换与字符串的正则表达式匹配的每个子字符串。
replaceAll()方法可以采用一个正则表达式或一个典型字符串作为第一个参数。这是因为一个典型的字符串本身就是一个正则表达式。
Java String valueOf()方法返回传递的参数的字符串表示形式。
valueOf()是一个静态方法。我们使用类名调用 valueof()方法,如下所示:String.valueOf(b);
菜鸟随记:这里忘得好多,这题的知识点都是见过的,但是还是不熟悉,他认识我,我不熟悉他,加油啊。
题目描述
实现删除字符串中出现次数最少的字符,若出现次数最少的字符有多个,则把出现次数最少的字符都删除。输出删除这些单词后的字符串,字符串中其它字符保持原来的顺序。
数据范围:输入的字符串长度满足 1 \le n \le 20 \1≤n≤20** ** ,保证输入的字符串中仅出现小写字母
输入描述:
字符串只包含小写英文字母, 不考虑非法输入,输入的字符串长度小于等于 20 个字节。
输出描述:
删除字符串中出现次数最少的字符后的字符串。
import java.util.Scanner;
import java.util.HashMap;
import java.util.Collection;//?
import java.util.Collections;//?
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String str = sc.nextLine();//?
char[] chars = str.toCharArray();
//统计每个字母的数量
HashMap<Character, Integer> map = new HashMap<>();//? 复习hashmap 有没有顺序
for (char c : chars) { //map.put;map.getOrDefault(c,0)
map.put(c, (map.getOrDefault(c, 0) + 1));
}
//找到数量最少的字符数量
Collection<Integer> values = map.values();
Integer min = Collections.min(values);
//用空字符串替换该字母
for (Character c : map.keySet()) {
if (map.get(c) == min) {
str = str.replaceAll(String.valueOf(c), "");//
}
}
System.out.println(str);
}
}
}
HJ24 合唱队#未完成难动态规划#
语法知识:
-
- 菜鸟随记:看来大学学的算法都忘了,一点印象都没有了。这道题并没有理解,难点在"//#"这里。
算法提示:
-
- 这里解释一下如何求最长递增子序列的长度(最长递减子序列长度同理)。对于第 i 位同学,从第 j 位(0 <= j < i)同学开始往前检查其身高是否比第 i 位同学矮,如果比第 i 位同学矮,说明 i 的前面可以增加一个比 i 矮的同学,那么以 i 为队尾的递增队伍长度可以在“以第 j 位同学为队尾的递增队伍长度”的基础上自增一个(把 i 添加在这个队伍后面)。如果这样计算得到的队长超过了当前以 i 结尾的最大递增队伍长度,那就将当前以 i 结尾的最大递增队伍长度转移过来。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) {
int n = sc.nextInt();
int[] arr = new int[n];
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
int[] left = new int[n];//左侧小于arr[i]的个数
int[] right = new int[n];//右侧小于arr[i]的个数
left[0]=1; //#最左边数设为1
right[n-1]=1; //#最右边数设为1
//计算每个位置左侧的最长递增
for(int i=0;i<n;i++){
left[i]=1;//#
for(int j=0;j<i;j++){
if(arr[i]>arr[j]){ //动态规划?
left[i]=Math.max(left[j]+1,left[i]);
}
}
}
//计算每个位置右侧的最长递减
for(int i=n-1;i>=0;i--){
right[i]=1;//#
for(int j=n-1;j>i;j--){
if(arr[i]>arr[j]){ //动态规划
right[i]=Math.max(right[i],right[j]+1);
}
}
}
//记录每个位置的值
int[] result= new int[n];
for(int i=0;i<n;i++){
//位置i计算了2次,所以需要 -1
result[i]=left[i]+right[i]-1;//#两个都包含本身
}
//找到最大的满足要求的值
int max=1;
for(int i=0;i<n;i++){
max=Math.max(result[i],max);
}
System.out.println(n-max);
}
}
}
//默写:解决2
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){//while(sc.hasNext())
int n=sc.nextInt();
int[] arr=new int[n];
int[] left=new int[n];
int[] right=new int[n];
int[] result=new int[n];
left[0]=1;//#
right[n-1]=1;//#
//读入学生身高
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();//与sc.nextLine()区别;
}
//计算位置左侧递增个数
for(int i=0;i<n;i++){
left[i]=1;
for(int j=0;j<i;j++){
if(arr[i]>arr[j]){
left[i]=Math.max(left[j]+1,left[i]);//left[i]+1
}
}
}
//计算位置右侧递减个数
for(int i=n-1;i>=0;i--){
right[i]=1;
for(int j=n-1;i<j;j--){
if(arr[i]>arr[j]){
right[i]=Math.max(right[i],right[j]+1);
}
}
}
//计算位置左右最多个数的和
int max=0;
for(int i=0;i<n;i++){
result[i]=left[i]+right[i] -1; //中间数少算了1次,可以从前两位理解
max=Math.max(result[i],max);
}
//输出需要剔除的最少人数
System.out.println(n-max);
}
}
}
//图片解法:原始版
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
int[] Q = new int[n];
for (int i = 0; i < n; i++) {
Q[i] = sc.nextInt();
}
//填左侧动态规划表
Main m4 = new Main();
int[] resl = m4.DTGHtable(Q);
//队列倒置后,填右侧动态规划表
int[] Q0 = Arrays.copyOf(Q, Q.length);
for (int j = 0; j < Q0.length; j++) {
Q0[j] = Q[Q.length - 1 - j];
};
int[] resr = m4.DTGHtable(Q0);
//左侧结果+右侧结果+1,排序后取最大值,输出(总人数-这个最大值)
int[] res = new int[Q.length];
for (int i = 0; i < res.length; i++) {
res[i] = resl[i + 1] + resr[resr.length - 1 - i] + 1;
}
Arrays.sort(res);
System.out.println(n - res[res.length - 1]);
}
}
//填动态规划表的方法
public int[] DTGHtable(int T[]) {
int n = T.length;
int[][] table = new int[n + 1][n + 1];
int[] Ti = Arrays.copyOf(T, n);
//首行首列设为0
for (int i = 0; i < n + 1; i++) {
table[i][0] = 0;
}
for (int j = 0; j < n + 1; j++) {
table[0][j] = 0;
}
//从第1列1行开始填
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
if (i < j && Ti[i - 1] < T[j - 1]) {//合适时的取值公式
table[i][j] = Math.max(1 + table[i - 1][i], table[i - 1][j]);
} else if ((i >= j) || (Ti[i - 1] >= T[j - 1])) {//不合适时的取值公式
table[i][j] = table[i - 1][j];
}
}
}
return table[table.length-1];
}
}
//默写:异常解决1
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){//while(sc.hasNext())
int n=sc.nextInt();
int[] arr=new int[n];
int[] left=new int[n];
int[] right=new int[n];
int[] result=new int[n];
left[0]=1;//#
right[n-1]=1;//#
//读入学生身高
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();//与sc.nextLine()区别;
}
//计算位置左侧递增个数
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(arr[i]>arr[j]){
left[i]=Math.max(left[j]+1,left[i]);//left[i]+1
}
}
}
//计算位置右侧递减个数
for(int i=n-1;i>=0;i--){
for(int j=n-1;i<j;j--){
if(arr[i]>arr[j]){
right[i]=Math.max(right[i],right[j]+1);
}
}
}
//计算位置左右最多个数的和
int max=0;
for(int i=0;i<n;i++){
result[i]=left[i]+right[i];//#中间数少算了1次,可以从前两位理解
max=Math.max(result[i],max);
}
//输出需要剔除的最少人数
System.out.println(n-max);
}
}
}
HJ25 数据分类处理
知识点:
不可以存储重复元素
没有索引
可以将元素按照规则进行排序
- TreeSet():根据其元素的自然排序进行排序--集合元素按升序(默认情况)排列
- TreeSet(Comparator comparator) :根据指定的比较器进行排序
Java 中 List。
List 是位于 java.util 下的一个接口,有序集合(也称为序列)。
- Java List.addAll()方法:添加所有元素到列表中
- 菜鸟随记:
算法提示:
根据题解可知:整数序列 I 和 规则整数序列 R
1、是根据 R 中元素到 I 序列中进行匹配查询并将 I 序列中出现的 R[i]的索引(index)和 I[i]的值进行记录
2、定义集合用于记录待查找条件 R[i]和 R[i]出现的次数(count),最后将第一步得到的集合放进来即可,此处也可使用 StringBuffer
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
int In = sc.nextInt();//整数序列I的个数
String[] I_arr = new String[In];//#
for (int i = 0; i < In; i++) {
I_arr[i] = String.valueOf(sc.nextInt());
}
int Rn = sc.nextInt();//整数序列R的个数
Set<Integer> R_set = new TreeSet<>(); //使用TreeSet进行排序和去重
for (int i = 0; i < Rn; i++) {
R_set.add(sc.nextInt());
}
List<Integer> I_list = new ArrayList<>(); //#用于存储整数序列 I
List<Integer> R_list = new ArrayList<>(); //#用于存储规则整数序列 R
for (int item : R_set) {//#
int count = 0; //统计R中元素 在I中出现的次数
for (int i = 0; i < I_arr.length; i++) {
if (I_arr[i].contains(String.valueOf(item))) {//#
count ++;
I_list.add(i);
I_list.add(Integer.valueOf(I_arr[i]));//#
}
}
if (count > 0) {
R_list.add(item);
R_list.add(count);//#
R_list.addAll(I_list);//? list.addAll
}
I_list.clear();
}
System.out.print(R_list.size() + " ");
for (Integer i : R_list) {//#
System.out.print(i + " ");
}
System.out.println();
}
}
}
HJ26 字符串排序
知识点:
Character.toLowerCase
方法用于将大写字符转换为小写。返回转换后字符的小写形式,如果有的话;否则返回字符本身。
Java 集合中 List 的 Sort()方法进行排序 #没整理清楚#
Java8 List.sort()排序常用方法 #没看完#
https://www.cnblogs.com/dukc/p/4755444.html
Comparable 的接口---------默认的比较规则---Comparable 自然排序。(由实体类实现)
<1> 实现该接口表示这个类的实例可以比较大小,可以进行自然的排序<2> 定义了默认的比较规则
<3> 其实现类需要实现 CompareTo()的方法
<4>CompareTon()的方法返回正数表示大,负数表示小,0 表示相等
Comparator 接口-----------临时的比较规则---Comparator 是定制排序。(无法修改实体类时,直接在调用方创建)
<1> 用于定义临时的比较规则,而不是默认的比较规则
<2> 其实现类需要实现 compara()方法
<3>Comparator 和 Comparable 都是 Java 集合框架的成员
stringbuilder.append():向其中添加元素
菜鸟随记:
这里的 Comparetor 和 Comparable 没看明白,看了好几篇都是蹭一蹭没进去,还需要继续整理。
得继续刷题了,这里就先放放,后面碰到相同的再去理解了
算法提示:
- StringBuilder and Comparator, 简单地忽略大小写排个序即可
import java.util.*;
public class Main {
public static String sort(String str) {
//先将英文字母收集起来
List<Character> letters = new ArrayList<>();//#
for (char ch : str.toCharArray()) {
if (Character.isLetter(ch)) {
letters.add(ch);
}
}
//#将英文字母先排序好toLowerCase,Comparator
letters.sort(new Comparator<Character>() {
public int compare(Character o1, Character o2) {
return Character.toLowerCase(o1) - Character.toLowerCase(o2);
}
});
//写法2: letters.sort((o1,o2)->Character.toLowerCase(o1)-Character.toLowerCase(o2));
//写法3-新的英文字母先排序处理: letters.sort(Comparator.comparing(Character::toLowerCase));
//若是非英文字母直接添加
StringBuilder result = new StringBuilder();//#
for (int i = 0, j = 0; i < str.length(); i++) {
if (Character.isLetter(str.charAt(i))) {
result.append(letters.get(j++));//#append
} else {
result.append(str.charAt(i));
}
}
return result.toString();
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextLine()) { // 注意 while 处理多个 case
String str = sc.nextLine();
String res = sort(str);
System.out.println(res);
}
}
}
HJ27 查找兄弟单词
知识点:371521,1988,6629
-
strA.equals(strB)与strA==strB 区别
- 菜鸟随记:
- 算法提示:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String[] strArr=sc.nextLine().split(" ");//注意" "
Integer n=Integer.parseInt(strArr[0]);
String x=strArr[strArr.length-2];
Integer k=Integer.parseInt(strArr[strArr.length-1]);
List<String> list=new ArrayList<>();
for(int i=1;i<=n;i++){
if(isBrother(x,strArr[i])){
list.add(strArr[i]);
}
}
int size=list.size();
System.out.println(size);
if(size>=k){//#Collections.sort(list)
Collections.sort(list);
System.out.println(list.get(k-1));
}
}
}
public static boolean isBrother(String x,String y){
if(x.length()!=y.length()||y.equals(x)){//strA.equals(strB);strA==strB 区别
return false;
}
char[] s=x.toCharArray();
char[] j=y.toCharArray();
Arrays.sort(s);//Arrays.sort(charArr)
Arrays.sort(j);
return new String(s).equals(new String(j));
}
}
HJ28 素数伴侣 #没看完#
知识点:
- 素数
- 菜鸟随记:还是没搞懂为啥是 i*i<=num,一直也没理解有懂的可以讲讲
算法提示:
import java.util.*; //这里包含了判断素数的方法 //小技巧!!!素数不是偶数,那么和是素数的话就是奇数+偶数 //那么可以分成两堆,一堆偶数,一堆奇数 //匈牙利算法,先到先得 能让就让 //有机会上,没机会创造机会也要上 public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 注意 hasNext 和 hasNextLine 的区别 while (sc.hasNext()) { // 注意 while 处理多个 case int n = sc.nextInt(); int[] tempArray=new int[n]; for(int i=0;i<n;i++){ tempArray[i]=sc.nextInt(); } List<Integer> evens=new ArrayList<>();//偶数序列 List<Integer> odds=new ArrayList<>();//奇数序列 for(int i:tempArray){//&按位与,判断奇偶性,1位奇数 if((i&1)!=1){ evens.add(i); }else{ odds.add(i); } } int[] evensMath=new int[evens.size()]; int result=0; //遍历奇数去匹配偶数 for(int i:odds){ //每一步重新创建,也就是相当于清空 //used数组用于标记某个偶数位置是否匹配过奇数 int[] used =new int[evens.size()]; //这里采用了匈牙利算法,先到先得 if(find(i,evens,used,evensMath)){ result++; } } System.out.println(result); } } public static boolean isPrime(int num){//素数判断 for(int i=2;i*i<=num;i++){//i*i<=num中的"="不能省略 if(num%i==0){ return false; } if(num==1){ return false; } } return true; } public static boolean find(int x,List<Integer> evens,int[] used,int[] evensMatch){ //遍历偶数 //去检查当前传入的奇数能否与偶数哪些数匹配 for(int i=0;i<evens.size();i++){ //如果当前偶数与传入的奇数匹配,并且当前偶数位还没有匹配过奇数 if(isPrime(x+evens.get(i))&&used[i]==0){ //设置当前偶数位匹配为true,也就是 1 used[i]=1; //如果第i个偶数没有伴侣 //或者第i个偶数原来有伴侣,并且该伴侣能够重新找到伴侣的话(这里有递归调用) //则奇数x可以设置为第i个偶数的伴侣 //这里采用了匈牙利算法,能让则让 if(evensMatch[i]==0||find(evensMatch[i],evens,used,evensMatch)){ evensMatch[i]=x; return true; } } } //遍历完偶数都没有可以与传入奇数做伴侣的,该奇数只能孤独终老了 return false; } }
HJ29 字符串加解密
知识点:
-
String.valueOf():valueOf() 返回值的字符串表示形
- 菜鸟随记:比较简单,可以参【21.HJ21 简单密码】来学习。21.HJ21 简单密码
- 算法提示:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
System.out.println(encode(sc.nextLine()));
System.out.println(decode(sc.nextLine()));
}
}
//加密函数
private static String encode(String toEnCode) {
char[] arr = toEnCode.toCharArray();
for (int i = 0; i < arr.length; i++) {
if (arr[i] >= 'a' && arr[i] < 'z') {
arr[i] = (char)(arr[i] - 'a' + 'A' + 1);
} else if (arr[i] == 'z') {
arr[i] = 'A';
} else if (arr[i] >= 'A' && arr[i] < 'Z') {
arr[i] = (char)(arr[i] - 'A' + 'a' + 1);
} else if (arr[i] == 'Z') {
arr[i] = 'a';
} else if (arr[i] >= '0' && arr[i] < '9') {
arr[i]=(char)(arr[i]+1);
}else if(arr[i]=='9'){
arr[i]='0';
}
}
return String.valueOf(arr);
}
//解密函数
private static String decode(String toDeCode) {
char[] arr=toDeCode.toCharArray();
for(int i=0;i<arr.length;i++){
if(arr[i]>'a'&&arr[i]<='z'){
arr[i]=(char)(arr[i]-'a'+'A'-1);
}else if(arr[i]=='a'){
arr[i]='Z';
}else if(arr[i]>'A'&&arr[i]<='Z'){
arr[i]=(char)(arr[i]-'A'+'a'-1);
}else if(arr[i]=='A'){
arr[i]='z';
}else if(arr[i]>'0'&&arr[i]<='9'){
arr[i]=(char)(arr[i]-1);
}else if(arr[i]=='0'){
arr[i]='9';
}
}
return String.valueOf(arr);
}
}
HJ30 字符串合并处理
知识点:
stringbuilder.replace(int start, int end, String str)
使用给定String
中的字符替换此序列的子字符串中的字符。
将字符串参数作为有符号的十进制整数进行解析。
如果方法有两个参数, 使用第二个参数指定的基数(进制数),将字符串参数解析为有符号的 10 进制整数。
- 菜鸟随记:越做越简单,加油,这个还是蛮难的,如果没有之前的打基础的话。重复碰到之前理解的知识点,碰到自己的理解误区,暂时决定继续记录之前记录过但是忘了的知识点,有更好的方法清评论。
- 算法提示:
//解法1
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String str1 = sc.next();
String str2 = sc.next();
System.out.println(mergeAndSortChange(str1,str2));
}
}
public static String mergeAndSortChange(String str1,String str2){
//1.合并
String str=str1+str2;
//2.排序
List<Character> odds=new ArrayList<>();//存放奇数字符序列
List<Character> evens=new ArrayList<>();//存放偶数字符序列
for(int i=0;i<str.length();i++){
if(i%2==0){
evens.add(str.charAt(i));
}else{
odds.add(str.charAt(i));
}
}
Collections.sort(evens);
Collections.sort(odds);
//重新拼接
StringBuilder sb=new StringBuilder();//字符串下标分奇偶
for(int i=0;i<evens.size();i++){//evens.size()>=odds.size()
sb.append(evens.get(i));
if(i<=odds.size()-1){//防止下标越界
sb.append(odds.get(i));
}
}
//3.字符转换
for(int i=0;i<sb.length();i++){
String s=sb.substring(i,i+1);
if(s.matches("[0-9a-fA-F]")){
//把16进制转为10进制,再转为二进制
StringBuilder binary=new StringBuilder(Integer.toBinaryString(Integer.parseInt(s,16)));
int len=binary.length();
for(int j=0;j<4-len;j++){//补零
binary.insert(0,"0");
}
binary=binary.reverse();//翻转
int decNum=Integer.parseInt(binary.toString(),2);//把2进制转为10进制
String hexStr=Integer.toHexString(decNum).toUpperCase();//转为成16进制字符串并大写
sb.replace(i,i+1,hexStr);//替换
}
}
return sb.toString();
}
}
//解法2:hash 保存十六进制反转的对应表, 空间换时间
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
//0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
// 0 8 4 C 2 A 6 E 1 9 5 D 3 B 7 F
Map<Character,Character> map = new HashMap<Character,Character>(){{
put('0', '0');
put('1', '8');
put('2', '4');
put('3', 'C');
put('4', '2');
put('5', 'A');
put('6', '6');
put('7', 'E');
put('8', '1');
put('9', '9');
put('a', '5');
put('b', 'D');
put('c', '3');
put('d', 'B');
put('e', '7');
put('f', 'F');
put('A', '5');
put('B', 'D');
put('C', '3');
put('D', 'B');
put('E', '7');
put('F', 'F');
}};
Scanner scanner = new Scanner(System.in);
String s = "";
while (scanner.hasNext()){
String s1 = scanner.next();
String s2 = scanner.next();
char[] ch = (s1 + s2).toCharArray();
//偶数位
char[]r1 = new char[ch.length / 2];
//奇数位
char[]r2 = new char[ch.length - ch.length / 2];
for (int i = 0, j = 0, k = 0; i < ch.length; i++){
if ( i % 2 == 0){
r2[j] = ch[i];
j++;
}else {
r1[k] = ch[i];
k++;
}
}
Arrays.sort(r1);
Arrays.sort(r2);
for (int i = 0, j = 0, k = 0; i < ch.length; i++){
if (i % 2 == 0){
//注意存在码表不包含的情况,使用原值
ch[i] = map.getOrDefault(r2[j], r2[j]);
j++;
}else {
ch[i] = map.getOrDefault(r1[k], r1[k]);
k++;
}
}
s = new String(ch);
System.out.println(s);
}
//return s;
}
}
HJ31 单词倒排
知识点:
string.trim()和 string.split(" ")、string.split()
String trim()
返回字符串的副本,忽略前导空白和尾部空白。String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。String replace(char oldChar, char newChar)
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。空字符串和空格字符和 null 区别
- 菜鸟随记:这个题目别人可能觉得很简单,但对我来说还是有些难度的。现在还处在一个抄题解理解的过程,有点不清楚什么时候的开始自己动手写比较好,目前还没有题感,继续抄题解了,哥们~
- 算法提示:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextLine()) { // 注意 while 处理多个 case
String str= sc.nextLine();
String res =Main.reverse(str);
System.out.println(res);
}
}
static String reverse(String str){
//匹配非字母的字符进行分割
String[] words=str.split("[^A-Za-z]+");//#
StringBuilder result=new StringBuilder();
//逆序添加分割完的单词
for(int i =words.length-1;i>=0;i--){
result.append(words[i]).append(" ");
}
return result.toString().trim();//#
}
}
HJ32 密码截取
最长回文子串的中心扩散法#
知识点:
-
- 菜鸟随记:有个动态规划的没去看,打算碰到再看连接在这,这个纯靠逻辑了,没哟调用这个那个的方法。
算法提示:最长回文子串的中心扩散法,遍历每个字符作为中间位,进行左右比较
解题思路:
从右到左,对每个字符进行遍历处理,并且每个字符要处理两次,因为回文子串有两种情况:
- ABA 型:只需要从当前字符向两边扩散,比较左右字符是否相等,找出以当前字符为中心的最长回文子串长度
- ABBA 型:只需要从当前字符和下一个字符向两边扩散,比较左右字符是否相等,找出以当前字符和下一个字符为中心的最长回文子串长度
最后比对两种类型的长度,取自较长的长度
复杂度分析:
时间复杂度:,需要遍历每个字符,复杂度为 O(N),对于每个字符的处理也需要 O(N) 的复杂度,因此总的时间复杂度为
空间复杂度:,只用到左右双指针,无需额外空间
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
String s=sc.nextLine();
System.out.println(solution(s));
}
private static int solution(String s){
int result=0;
for(int i=0;i<s.length();i++){
//ABA型
int len1=longest(s,i,i);
//ABBA型
int len2=longest(s,i,i+1);
result=Math.max(result,len1>len2?len1:len2);//#写的好
}
return result;
}
private static int longest(String s,int l,int r){
while(l>=0&&r<s.length()&&s.charAt(l)==s.charAt(r)){
l--;
r++;
}
return r-l-1;//#(r-l)+1-2
}
}
HJ33 整数与 IP 地址间的转换
#灵活的漏勺#
题目
原理:ip 地址的每段可以看成是一个 0-255 的整数,把每段拆分成一个二进制形式组合起来,然后把这个二进制数转变成
一个长整数。
举例:一个 ip 地址为 10.0.3.193
每段数字 相对应的二进制数
10 00001010
0 00000000
3 00000011
193 11000001组合起来即为:00001010 00000000 00000011 11000001,转换为 10 进制数就是:167773121,即该 IP 地址转换后的数字就是它了。
数据范围:保证输入的是合法的 IP 序列
输入描述:
输入
1 输入 IP 地址
2 输入 10 进制型的 IP 地址输出描述:
输出
1 输出转换成 10 进制的 IP 地址
2 输出转换后的 IP 地址示例 1
输入:
10.0.3.193 167969729
复制
输出:
167773121 10.3.3.193
知识点:
注意加号左右两边的类型,一个为字符串,一个为数字时,转换为字符串类型。此处“+”表示拼接的意思。
- 菜鸟随记:这也算是取巧了吗?这本来就存在这样的巧啊,不能取吗?注意,规律啊
- 算法提示:每个 . 之间的进制是 256,所以前三段的数字都要乘 256,最后一个数字不用
import java.util.*;
public class Main {
private final int N=4;
public String convert(String str){
//ipv4 -> int
if(str.contains(".")){
String[] fields=str.split("\\.");
long result = 0;
for(int i=0;i<N;i++){
//每个 . 之间的进制是256,所以前三段的数字都要乘256,最后一个数字不用
result=result*256+Integer.parseInt(fields[i]);//#转换为10进制数
}
return ""+result;//返回字符串
}
// int -> ipv4
else{
Long ipv4=Long.parseLong(str);
String result="";
for(int i=0;i<N;i++){
result=ipv4%256+"."+result;//#求余
ipv4/=256;//#整除
}
return result.substring(0,result.length()-1);//#去掉最后一个.
}
}
public static void main(String[] args) {
Main solution=new Main();
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String str = sc.next();
String res = solution.convert(str);
System.out.println(res);
}
}
}
HJ34 图片整理
#简单#
题目
描述
Lily 上课时使用字母数字图片教小朋友们学习英语单词,每次都需要把这些图片按照大小(ASCII 码值从小到大)排列收好。请大家给 Lily 帮忙,通过代码解决。
Lily 使用的图片使用字符"A"到"Z"、"a"到"z"、"0"到"9"表示。
数据范围:每组输入的字符串长度满足 1 n 1000
输入描述:
一行,一个字符串,字符串中的每个字符表示一张 Lily 使用的图片。
输出描述:
Lily 的所有图片按照从小到大的顺序输出
示例 1
输入:
Ihave1nose2hands10fingers
复制
输出:
0112Iaadeeefghhinnnorsssv
知识点:
-
- 菜鸟随记:第一种很巧妙,不过也不是第一次见这么,写法了。第二种就是考验对响应工具类的熟练程度了
- 算法提示:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
int a[]=new int[128];
String str=sc.nextLine();
for(int i=0;i<str.length();i++){
int k=str.charAt(i);
a[k]++;//统计出现次数
}
for(int j=48;j<a.length;j++){//从'0'开始输出
if(a[j]!=0){
for(int b=0;b<a[j];b++){
System.out.print((char)j);
}
}
}
System.out.println();
}
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String str=sc.nextLine();
char[] charArr=str.toCharArray();
Arrays.sort(charArr);
for(char c:charArr){
System.out.print(c);
}
System.out.println();
}
}
}
HJ35 蛇形矩阵
#难# #纯思路#
- 题目
- 菜鸟随记:这个题我是真的有点难理解,第一个题解想了 1 个小时了,边想边走神 o(╥﹏╥)o,今天看不下去了,明天再看吧,伤心。过去了 6 天了,我卡在这个问题这里,不想去面对。然而今天我又回来了,干他娘的。解决了但是思路有点乱,先这样吧。再总结其实没啥知识点就是思路的问题。
算法提示:解题思路:方案有两个。
- 方案一:输入矩阵行数,创建一维数组;再遍历行数,每走到某行后,都向其右上方继续行进,在此期间用 value 来控制当前行进位置的数值;最后遍历二维数组,若值为 0 则不输出,即可完成。
- 方案二:找蛇形矩阵的数学关系。先看最外围的关系,第一行最后值为等差数列的和,即(N*N+N)/2,其中 N 为输入的行数 row,每行的最后一个值都是和再减去当前行数再加一;再看其他位置的关系,第一行倒数第二个值的 N 变为了 row-1,即 row-1 的等差数列和,第二行倒数第二个值是和再减去当前行数再加一,那么可以用 i+j-1 来等同于 row-1,比如第一行第三列的 i+j-1 为 3,第二行第二列的 i+j-1 依然为 3,区别在于第二行第二列的数值是长度为 3 的等差数列和再减行数(2)再加一;以此类推,完成。
//方案1:直接循环走对角线 难
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
int n = sc.nextInt();//读入正整数n
int[][] result = new int[n][]; //建立数组(n行)
int t = 1; //记录依次赋予的数组值
for (int i = 0; i < n; i++) {
result[i] = new int[n - i];//#数组第i行有第n-i个元素
//result[0][0]=1 ->result[1][0]=2 ->result[0][1]=3
for (int j = 0; j < i + 1; j++) {//#对第i个对角线赋值
result[i - j][j] = t;//#行递减。列递增,合并
t++;//#
}
}
//输出数组
for (int[] a : result) {
for (int a1 : a) {
System.out.print(a1 + " ");
}
System.out.println();
}
}
}
}
//方案1:中
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
int n = sc.nextInt();//读入正整数n
int[][] result = new int[n][]; //建立数组(n行)
int t = 1; //记录依次赋予的数组值
for (int i = 0; i < n; i++) {
result[i] = new int[n - i];//#数组第i行有第n-i个元素
int k=i;
for (int j = 0; j <= i; j++) {//#j<=i是因为对称
result[k][j] = t;
t++;//#
k--;
}
}
//输出数组
for (int[] a : result) {
for (int a1 : a) {
System.out.print(a1 + " ");
}
System.out.println();
}
}
}
}
//方案一:最容易理解
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
int n = sc.nextInt();//读入正整数n
int value = 1;
int[][] result = new int[n][];
for (int i = 0; i < n; i++) {
result[i] = new int[n - i];
int a = i; //行
int j = 0; //列
for(a=i,j=0;a>=0&&j<n;a--,j++){//#对第i个对角线赋值
result[a][j]=value;//#行递减。列递增,合并
value++;
}
}
//输出数组
for (int[] a : result) {
for (int a1 : a) {
System.out.print(a1 + " ");
}
System.out.println();
}
}
}
}
```
//方案2
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
int n = sc.nextInt();//读入正整数n
int row=n;
for (int i = 1; i <= row; i++) {//行
for (int j = 1; j <row-(i-1); j++) {//列
System.out.print(((j+i-1)*(j+i-1)+j+i-1)/2-(i-1)+" ");
}
System.out.println((row*row+row)/2-(i-1));
}
}
}
}
HJ36 字符串加密
题目
描述
有一种技巧可以对数据进行加密,它使用一个单词作为它的密匙。下面是它的工作原理:首先,选择一个单词作为密匙,如 TRAILBLAZERS。如果单词中包含有重复的字母,只保留第 1 个,将所得结果作为新字母表开头,并将新建立的字母表中未出现的字母按照正常字母表顺序加入新字母表。如下所示:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
T R A I L B Z E S C D F G H J K M N O P Q U V W X Y (实际需建立小写字母的字母表,此字母表仅为方便演示)
上面其他用字母表中剩余的字母填充完整。在对信息进行加密时,信息中的每个字母被固定于顶上那行,并用下面那行的对应字母一一取代原文的字母(字母字符的大小写状态应该保留)。因此,使用这个密匙, Attack AT DAWN (黎明时攻击)就会被加密为 Tpptad TP ITVH。
请实现下述接口,通过指定的密匙和明文得到密文。
数据范围: ,保证输入的字符串中仅包含小写字母
输入描述:
先输入 key 和要加密的字符串
输出描述:
返回加密后的字符串
示例 1
输入:
nihao ni
输出:
le
知识点:
- 集合类型主要有 3 种:set(集)、list(列表)和 map(映射)
Character
- 菜鸟随记:
算法提示:
Java 版本的字符串加密
- 思路是利用有序的 LinkedHashSet 集合,先把密钥添加进去,再把剩下的字符添加进去形成完整的密钥;
- 再根据要加密的字符串字符一个个取出拼接输出,ps:不要忘了大小写和空格
import java.util.Scanner;
import java.util.LinkedHashSet;//集合
import java.util.ArrayList;//列表
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
String s1 = sc.nextLine().toUpperCase();
String s2 = sc.nextLine();
char[] charArr1 = s1.toCharArray();
char[] charArr2 = s2.toCharArray();
LinkedHashSet<Character> set = new LinkedHashSet();
for (int i = 0; i < charArr1.length; i++) {
set.add(charArr1[i]);
}
int k = 0;
while (set.size() < 26) {
char a = (char)('A' + k);
set.add(a);
k++;
}
ArrayList<Character> list = new ArrayList<>(set);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < charArr2.length; i++) {
if (charArr2[i] == ' ') {
sb.append(charArr2[i]);
} else if (charArr2[i] < 'a') {
int n = (int) (charArr2[i] - 'A'); //#
char c = list.get(n);
sb.append(c);
} else {
int n = (int) (charArr2[i] - 'a');
char c = (char)(list.get(n) + 'a' - 'A');
sb.append(c);
}
}
System.out.println(sb.toString());
}
}
}
HJ37 统计每个月兔子的总数
#难# #纯思路#
- 题目
- 知识点:
- 菜鸟随记:不是很能理解方法 1 中的递推,同时也不是很能理解方法 2 中的递归
算法提示:
方法一:递推
解题思路:
利用动态规划的思想,通过规律进行递推,找出
算法流程:
分别用三个变量表示:一、二、三月龄兔子数量,addVal 保存下个月将繁殖的兔子数量
第二个月开始递推:
- 三月龄及以上兔子总数 = 二月龄兔子总数 + 原本三月龄及以上兔子总数
- 二月龄兔子总数 = 上个月的一月龄兔子总数
- 一月龄(即这个月出生)兔子总数 = 上个月将繁殖的兔子数量、
- 下个月将出生的兔子 = 下个月成为三月龄及以上的兔子数量
最后返回第 N 个月的总量即可
复杂度分析:
时间复杂度:, N 为输入的月数,需要遍历 [2…N]
空间复杂度:,只用到了几个变量表示三个状态
方法二:递归
解题思路:
递归中可以通过备忘录减少重叠子问题
算法流程:
维护一个备忘录,用来保存计算过的状态,减少重叠子问题
明确定义递归函数:返回第 n 个月兔子总量
递归终止条件:n == 1 或 n == 2 时,即第一和第二个月的兔子总数为 1
如果备忘录已经保存了该月份的数据,表示已经计算过,直接返回即可,不需要再计算
根据规律以及函数定义,进行递归调用,同时保存状态到备忘录
设第 n 个月的兔子数量为 num[n],第 n 个月有第 n-1 个月已有的兔子,同时,可能会有新出生的兔子。由题目可知,每只兔子在第三个月都会生一只兔子,所以第 n 个月出生的兔子数量等于第 n-2 月时的兔子数量,即 num[n]=num[n-1]+num[n-2]。
复杂度分析:
时间复杂度:,子问题经过备忘录优化后为 N 个子问题,子问题处理的时间复杂度为 ,因此总的时间复杂度为
空间复杂度: ,递归所需要的栈的深度
//方法1:递推
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
System.out.println(solution(scanner.nextInt()));
}
}
private static int solution(int month) {
// 第一个月初始化
// 一月龄兔子总数
int oneMonth = 1;
// 二月龄兔子总数
int twoMonth = 0;
// 三月龄及以上兔子总数
int threeMonth = 0;
// 下个月将繁殖的兔子数量
int addVal = 0;
// 第二个月开始递推, i表示第i个月
for(int i = 2; i <= month; i++) {
// 三月龄及以上兔子总数 = 二月龄兔子总数 + 原本三月龄及以上兔子总数
threeMonth += twoMonth;
// 二月龄兔子总数 = 上个月的一月龄兔子总数
twoMonth = oneMonth;
// 一月龄(即这个月出生)兔子总数 = 上个月将繁殖的兔子数量
oneMonth = addVal;
// 下个月将出生的兔子 = 下个月成为三月龄及以上的兔子数量
addVal = twoMonth + threeMonth;
}
return (oneMonth + twoMonth + threeMonth);
}
}
//方案2:递归
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNext()) { // 注意 while 处理多个 case
int month=sc.nextInt();
//#? 备忘录里的元素都初始化为-1
memo=new int [month+1];
Arrays.fill( memo,-1);
System.out.println(dp(month));
}
}
// 备忘录,减少重叠子问题
private static int[] memo;
// 返回第n个月兔子总量
private static int dp(int m){
// 递归终止条件
if(m<=2){
return 1;
}
// 使用备忘录
if(memo[m] !=-1){
return memo[m];
}
//# 递归调用,满足规律
memo[m]=dp(m-1)+dp(m-2);
return memo[m];
}
}
HJ38 求小球落地 5 次后所经历的路程和第 5 次反弹的高度
#灵活的漏勺#
描述
假设一个球从任意高度自由落下,每次落地后反跳回原高度的一半; 再落下, 求它在第 5 次落地时,共经历多少米?第 5 次反弹多高?
数据范围:输入的小球初始高度满足 ,且保证是一个整数
输入描述:
输入起始高度,int 型
输出描述:
分别输出第 5 次落地时,共经过多少米以及第 5 次反弹多高。
注意:你可以认为你输出保留六位或以上小数的结果可以通过此题。示例 1
输入:
1
输出:
2.875 0.03125
- 知识点:
- 菜鸟随记:也是个大漏勺。发现好像自己这样抄题解的作用越来越小了,还产生了依赖性,要试着自己解决问题。
- 算法提示:
//方案1:钻漏洞,答案是 示例的整倍数
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
double init=sc.nextDouble();
System.out.println(init*2.875);
System.out.println(init*0.03125);
}
}
}
//方案2:直接计算
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (sc.hasNextInt()) { // 注意 while 处理多个 case
double h=sc.nextDouble();
double temp=h/2;
for(int i=1;i<5;i++){//#循环了4次,第四次i=4,i++,i=5不满足
h+=temp*2;
temp=temp/2;
}
System.out.println(h);
System.out.println(temp);
}
}
}
HJ39 判断两个 IP 是否属于同一子网
#IP与子网掩码#
题目
描述
IP 地址是由 4 个 0-255 之间的整数构成的,用"."符号相连。
二进制的 IP 地址格式有 32 位,例如:10000011,01101011,00000011,00011000;每八位用十进制表示就是 131.107.3.24
子网掩码是用来判断任意两台计算机的 IP 地址是否属于同一子网络的根据。
子网掩码与 IP 地址结构相同,是 32 位二进制数,由 1 和 0 组成,且 1 和 0 分别连续,其中网络号部分全为“1”和主机号部分全为“0”。
你可以简单的认为子网掩码是一串连续的 1 和一串连续的 0 拼接而成的 32 位二进制数,左边部分都是 1,右边部分都是 0。
利用子网掩码可以判断两台主机是否在同一子网中。
若两台主机的 IP 地址分别与它们的子网掩码进行逻辑“与”运算(按位与/AND)后的结果相同,则说明这两台主机在同一子网中。
示例:
I P 地址 192.168.0.1
子网掩码 255.255.255.0转化为二进制进行运算:
I P 地址 11000000.10101000.00000000.00000001
子网掩码 11111111.11111111.11111111.00000000AND 运算 11000000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0I P 地址 192.168.0.254
子网掩码 255.255.255.0转化为二进制进行运算:
I P 地址 11000000.10101000.00000000.11111110
子网掩码 11111111.11111111.11111111.00000000AND 运算 11000000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0通过以上对两台计算机 IP 地址与子网掩码的 AND 运算后,我们可以看到它运算结果是一样的。均为 192.168.0.0,所以这二台计算机可视为是同一子网络。
输入一个子网掩码以及两个 ip 地址,判断这两个 ip 地址是否是一个子网络。
若 IP 地址或子网掩码格式非法则输出 1,若 IP1 与 IP2 属于同一子网络输出 0,若 IP1 与 IP2 不属于同一子网络输出 2。
注:
有效掩码与 IP 的性质为:
- 掩码与 IP 每一段在 0 - 255 之间
- 掩码的二进制字符串前缀为网络号,都由‘1’组成;后缀为主机号,都由'0'组成
输入描述:
3 行输入,第 1 行是输入子网掩码、第 2,3 行是输入两个 ip 地址
题目的示例中给出了三组数据,但是在实际提交时,你的程序可以只处理一组数据(3 行)。输出描述:
若 IP 地址或子网掩码格式非法则输出 1,若 IP1 与 IP2 属于同一子网络输出 0,若 IP1 与 IP2 不属于同一子网络输出 2
示例 1
输入:
255.255.255.0 192.168.224.256 192.168.10.4 255.0.0.0 193.194.202.15 232.43.7.59 255.255.255.0 192.168.0.254 192.168.0.1
输出:
1 2 0
说明:
对于第一个例子: 255.255.255.0 192.168.224.256 192.168.10.4 其中IP:192.168.224.256不合法,输出1 对于第二个例子: 255.0.0.0 193.194.202.15 232.43.7.59 2个与运算之后,不在同一个子网,输出2 对于第三个例子,2个与运算之后,如题目描述所示,在同一个子网,输出0
知识点:
- String.format("%08d",Integer.parseInt(binary));
- 菜鸟随记:又是关于 IP 和子网掩码的
- 算法提示:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
String mask = in.next();
String ip1 = in.next();
String ip2 = in.next();
if (!isValidMask(mask) || !isValidIp(ip1) || !isValidIp(ip2)) {
System.out.println(1);
} else if (isSameSubnet(mask, ip1, ip2)) {
System.out.println(0);
} else {
System.out.println(2);
}
}
}
private static boolean isValidIp(String ip) {
String[] ipi = ip.split("\\.");
if (ipi.length != 4) {
return false;
}
for (String s : ipi) {
if (Integer.parseInt(s) < 0 || Integer.parseInt(s) > 255) {
return false;
}
}
return true;
}
private static boolean isValidMask(String mask) {
if (!isValidIp(mask)) {
return false;
}
String[] maski = mask.split("\\.");
//将mask的每个网络号和主机号转化为2进制串,不足8位的前面补
StringBuilder sb = new StringBuilder();
for (String ss : maski) {
String binary = Integer.toBinaryString(Integer.parseInt(ss));
binary = String.format("%08d",
Integer.parseInt(binary)); //#不足的8位的补齐
sb.append(binary);
}
//#最后一个1在第一个0之前
return sb.toString().lastIndexOf("1") < sb.toString().indexOf("0");
}
private static boolean isSameSubnet(String mask, String ip1, String ip2) {
String[] maski = mask.split("\\.");
String[] ip1i = ip1.split("\\.");
String[] ip2i = ip2.split("\\.");
for (int i = 0; i < maski.length; i++) {
if ((Integer.parseInt(maski[i]) & Integer.parseInt(ip1i[i])) !=
(Integer.parseInt(maski[i]) & Integer.parseInt(ip2i[i])) ) {
return false;
}
}
return true;
}
}
HJ40 统计字符
#简单#
题目
描述
输入一行字符,分别统计出包含英文字母、空格、数字和其它字符的个数。
数据范围:输入的字符串长度满足
输入描述:
输入一行字符串,可以有空格
输出描述:
统计其中英文字符,空格字符,数字字符,其他字符的个数
示例 1
输入:
1qazxsw23 edcvfr45tgbn hy67uj m,ki89ol.\\/;p0-=\\][
输出:
26 3 10 12
- 知识点:
- 菜鸟随记:
算法提示:
思路:依次清除英文字母、空格、数字。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String str=in.nextLine();
String s1=str.replaceAll("[a-z]+|[A-Z]+","");
System.out.println(str.length()-s1.length());
String s2=s1.replaceAll(" ","");
System.out.println(s1.length()-s2.length());
String s3=s2.replaceAll("[0-9]+","");
System.out.println(s2.length()-s3.length()+"\n"+s3.length());
}
}
}
HJ41 称砝码
#字符串# #哈希#
题目
描述
现有 n 种砝码,重量互不相等,分别为 m1,m2,m3…mn ;
每种砝码对应的数量为 x1,x2,x3...xn 。现在要用这些砝码去称物体的重量(放在同一侧),问能称出多少种不同的重量。注:
称重重量包括 0
数据范围:每组输入数据满足 , ,
输入描述:
对于每组测试数据:
第一行:n --- 砝码的种数(范围[1,10])
第二行:m1 m2 m3 ... mn --- 每种砝码的重量(范围[1,2000])
第三行:x1 x2 x3 .... xn --- 每种砝码对应的数量(范围[1,10])输出描述:
利用给定的砝码可以称出的不同的重量数
示例 1
输入:
2 1 2 2 1
输出:
5
说明:
可以表示出0,1,2,3,4五种重量。
知识点:https://blog.csdn.net/m0_64886876/article/details/126511997
hashset 有顺序吗
- 无序的:是说存入的顺序和他内部的顺序并不相同,所以看起来是"无序的",内部会用特定的方法排序。
- 菜鸟随记:这个题目挡了我有个 3-4 个夜晚了,就是有些畏难不想面对,当然那几个夜晚到是也没闲着,去做了一些简单的事。今天我又回来了干他。这里涉及到 set 和 list 的知识比较重要。在 IDEA 中单步调试后懂了怎么运行的,但是让我自己去重新写一个新的这种思路我不一定写的出来。试试默写一遍思路,这个方法用几天试试。
- 算法提示:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
HashSet<Integer> set = new
HashSet<>(); //存放所有可能的结果,不用担心重复问题
set.add(0);//由示例可知,0重量也属于一种情况
int n = in.nextInt(); //砝码的种数(范围[1,10])
int[] w = new int[n];
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
w[i] = in.nextInt(); //每种砝码的重量(范围[1,2000])
}
for (int i = 0; i < n; i++) {
nums[i] = in.nextInt(); //每种砝码对应的数量(范围[1,10])
}
for (int i = 0; i < n; i++) {
ArrayList<Integer> list = new ArrayList<>(set); //取当前set存放的所有斤数结果
for (int j = 1; j <= nums[i]; j++) { //遍历当前种类砝码个数
for (int k = 0; k < list.size(); k++) { //遍历当前set的结果
set.add(list.get(k) + w[i] * j); //旧有斤数+砝码斤数
}
}
}
System.out.println(set.size());
}
}
}
HJ42 学英语
#字符串# #难#
题目
描述
Jessi 初学英语,为了快速读出一串数字,编写程序将数字转换成英文:
具体规则如下:
1.在英语读法中三位数字看成一整体,后面再加一个计数单位。从最右边往左数,三位一单位,例如 12,345 等
2.每三位数后记得带上计数单位 分别是 thousand, million, billion.
3.公式:百万以下千以上的数 X thousand X, 10 亿以下百万以上的数:X million X thousand X, 10 亿以上的数:X billion X million X thousand X. 每个 X 分别代表三位数或两位数或一位数。
4.在英式英语中百位数和十位数之间要加 and,美式英语中则会省略,我们这个题目采用加上 and,百分位为零的话,这道题目我们省略 and下面再看几个数字例句:
22: twenty two100: one hundred
145: one hundred and forty five
1,234: one thousand two hundred and thirty four
8,088: eight thousand (and) eighty eight (注:这个 and 可加可不加,这个题目我们选择不加)
486,669: four hundred and eighty six thousand six hundred and sixty nine
1,652,510: one million six hundred and fifty two thousand five hundred and ten说明:
数字为正整数,不考虑小数,转化结果为英文小写;
保证输入的数据合法
关键字提示:and,billion,million,thousand,hundred。数据范围:1≤n≤2000000
输入描述:
输入一个 long 型整数
输出描述:
输出相应的英文写法
示例 1
输入:
22
输出:
twenty two
- 知识点:
- 菜鸟随记:还是直接抄的,用时 1.5h。有点不太想默写,要不试试代码填空。
算法提示:
- 小于 20 的直接读数,如果个位和十位是 0,则不需要读数(正百的 100,例:one hundred);如果有百位读取百位,如果个位和十位是 0,则不需要加 and,否则需要加 and,加 hubdred,加百位
- 大于 20 的,依次添加个位,十位,百位;添加百位的时候,先加 and,加 hundred,加百位
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String[] OneDigit = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"};
String[] TenDigit = {"zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
String[] CountDigit = {"", "hundred", "thousand", "million", "billion"};
while (in.hasNext()) { // 注意 while 处理多个 case
String line = in.nextLine();
StringBuilder sb = new StringBuilder(); //
ArrayList lists = new ArrayList<String>();
if (!line.matches("\\d+")) {//如果匹配的不是数字
System.out.println("error");
}
int lineNum = Integer.parseInt(line);
int power = 1;//单位
while (lineNum != 0) {
if (power != 1) {
lists.add(CountDigit[power]);
}
int t=lineNum%1000;//取低三位
if(t%100 <=20){//注意小于20,直接读
if(t%100 !=0){//十位和个位是零的话就不需要读数了
lists.add(OneDigit[t%100]);
}
if(t/100 !=0){//有百位
if(t%100 !=0){//十位和个位是零的话就不需要添加and了
lists.add("and");
}
lists.add("hundred");
lists.add(OneDigit[t/100]);
}
}else{//大于20
// 有个位
if(t%10 !=0){
lists.add(OneDigit[t%10]);
}
t/=10;
// 有十位
if(t%10 !=0){
lists.add(TenDigit[t%10]);
}
t/=10;
// 有百位
if (t%10 !=0) {
lists.add("and");
lists.add("hundred");
lists.add(OneDigit[t%10]);
}
}
lineNum /=1000;//每次缩小1000倍
power++;//单位*1000
}
//添加的时候,先添加低位,读数的时候先读高位,倒着读
for (int i = lists.size()-1; i >=0; i--) {
if(i !=0){
sb.append(lists.get(i)+" ");
}else {
sb.append(lists.get(i));//最后一个不加空格
}
}
System.out.println(sb.toString());
}
}
}
HJ43 迷宫问题
#BFS# #DFS# #递归#
#算法经典题# #重要知识点#
题目
描述
定义一个二维数组 N*M ,如 5 × 5 数组下所示:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};它表示一个迷宫,其中的 1 表示墙壁,0 表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线。入口点为[0,0],既第一格是可以走的路。
数据范围: , 输入的内容只包含
输入描述:
输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的 1 表示墙壁,0 表示可以走的路。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
输出描述:
左上角到右下角的最短路径,格式如样例所示。
示例 1
输入:
5 5 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0
输出:
(0,0) (1,0) (2,0) (2,1) (2,2) (2,3) (2,4) (3,4) (4,4)
示例 2
输入:
5 5 0 1 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0
输出:
(0,0) (1,0) (2,0) (3,0) (4,0) (4,1) (4,2) (4,3) (4,4)
说明:
注意:不能斜着走!!
知识点:
Pos
map.length:行数
map[0].length:列数
Queue:https://www.nhooo.com/java/java-queue.html
queue.offer()
将指定的元素插入队列。如果任务成功,则 offer()返回 true,否则返回 false。
如果可以在不违反容量限制的情况下立即将指定的元素插入到此队列中。使用容量受限队列时,此方法通常最好添加,因为仅通过引发异常才能插入元素。
Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions. When using a capacity-restricted queue, this method is generally preferable to add, which can fail to insert an element only by throwing an exception.
形参:
e – the element to add
返回值:
true if the element was added to this queue, else falsequeue.poll()
返回并删除队列的开头。 如果队列为空,则返回 null。
检索并删除此队列的头部,如果此队列为空,则返回 null。
Retrieves and removes the head of this queue, or returns null if this queue is empty.
返回值:
the head of this queue, or null if this queue is emptyStack:https://www.nhooo.com/java/java-stack.html
stack.pop();
要从堆栈顶部删除元素,我们使用 pop()方法。
- Removes the object at the top of this stack and returns that object as the value of this function.
返回值:
The object at the top of this stack (the last item of the Vector object).
抛出:
EmptyStackException – if this stack is empty.stack.push()
要将元素添加到堆栈的顶部,我们使用 push()方法
- Pushes an item onto the top of this stack. This has exactly the same effect as:
addElement(item)
形参:
item – the item to be pushed onto this stack.
返回值:
the item argument.
请参阅:
Vector.addElementstack.peek();
该 peek()方法从堆栈顶部返回一个对象。
- Looks at the object at the top of this stack without removing it from the stack.
返回值:
the object at the top of this stack (the last item of the Vector object).
抛出:
EmptyStackException – if this stack is empty.Stack 是栈,queue 是队列;
栈是后进先出,队列是先进先出;
栈是出入从同一个位置;
队列是入从结构的一端进入,从另一端出队;
栈就像一个盒子,你把物体依次放入后,能先拿出来的只能是上面最后放进去的,下层的想要拿出需要将上层的先拿出,也就是先出栈;
队列是一个胡同,人们都进入胡同了,只有最前面的人从胡同出口出去,后面的人只有等前面的人走完后才能依次通过。
链接:https://www.nowcoder.com/questionTerminal/03dff15ecbc348c6bf62c6fee8cfcf3e?toCommentId=537192
来源:牛客网DFS 和 BFS 异同
一、深度优先搜索(dfs)
类似树从干路到支路的先序遍历方式,形象的说就是一条路走到黑,如果走到头了,则返回上一个节点,所以我们很自然会想到用递归实现,另外加一个标记数组,确保节点只访问一次。(这里建议邻接矩阵跟标记数组全部使用全局变量,不然的话函数传参会很麻烦),每次递归后都会找到与之相邻的节点,并再次递归,直到所有节点全部遍历一次。
① 概念理解:一个人迷路,遇到很多分叉路口,他只有一个人,并且想走出去,所以只能一个个尝试,一条道路走到黑,发现到头了,然后再拐回去走刚才这条路的其他分叉路口,最后发现这条路的所有分叉路口走完了,选择另外一条路继续以上操作,直到所有的路都走过了。实际上就是往深度走,走错了就回来,找没走过的路,直到走到终点。
② 实现方式:堆栈实现,
③ 优点:能找出所有解决方案;相对于 bfs 内存占用少。
④ 缺点:找到的路径有可能不是最短路径,且在深度较大时效率低。
二、广度优先搜索(bfs)
所谓广度优先就类似树里面的层序遍历,每次访问的是同层的节点,同层访问后再继续向下访问,这里我们用队列来实现,利用队列的先进先出性质,每次递增访问,这样通过广度优先,我们可以得到迷宫这样题型的最短路径。
① 概念理解:一个人迷路,但是他有技能(分身术)它遇到分叉路口,不是选一个走,而是分身多个人都试试,比如有 A、B、C 三个分叉路口,它 A 路走一步,紧接着 B 路也走一步,然后 C 路也赶紧走一步,步伐整齐统一,直到所有的路走过了。实际上就是往四周搜索,分开搜索。
② 实现方式:队列实现
③ 优点:找到的路径就是最短路径,效率高。
④ 缺点:内存耗费大。
dfs 和 bfs 的最优解情况
我们在具体是算法题目上选择哪种搜索方法还是要结合实际的,如果问题是能不能,那么深搜大部分就可以满足了,但是如果涉及到了最短/最小/最大等,广搜比较适合。
形象点说,DFS 是线,BFS 是面;DFS 是单打独斗,BFS 是集体行动。
其实两者的应用场景差不多:都是在不断的做选择的情境下使用的
① 比较两种算法:广度(bfs)一般无回溯操作,即人栈和出栈的操作,所以运行速度比深度优先搜索法要快些。所以一般情况下,深度(dfs)占内存少但速度较慢,广度(bfs)占内存较多但速度较快,在距离与深度成正比的情况下能较快地求出最优解。
② 如果数据量较大,必须考虑溢出和节省内存空间的问题,使用深度优先搜索法较好,深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归形式较明显时,用递归方法设计的较好,它可以使得程序结构更简捷易懂。但当搜索深度较大时,当数据量较大时,由于系统堆栈容量的限制,递归易产生溢出,用非递归方法设计比较好
③ 如果数据量较小,且对程序运行的效率要求较高,或者题意是需要找出最短路径,一般使用广度优先搜索法。
菜鸟随记:终于是再次碰到 DFS 和 BFS 之前都是单个碰到,但是好像并没有注意到有什么不同的,通过代码比较(我得再细看看),BFS 方法又用了快 1.5 小时(对一共 3 个多小时)。
今天(第 3 天。对,你没想错)单步调试了一下代码花了差不多 1.5 小时,认真理解了一下,收获蛮多的,最主要的是克服了对递归的恐惧和迷惑和对示例 BFS 的深入理解(BFS 要递归的方式来打印节点的原因在于 Point 类的结构,每个 Point 都带着她是如何来到这个位置的,这个过程需要回溯才能找到她的出发点,这也是为啥用递归的方式打印终点位置的 point 的原因)。
算法提示:
/思路:广度优先遍历矩阵。代价相同的图中,广度优先遍历可以保证遍历到的目标点就是经过最短路径到达的点。为此,我们可以创建一个 Point 类,属性为横纵坐标和父节点。从(0,0)出发,将经过的坐标点都设为 1,避免重复经过而进入死循环。把当前点的上下左右值为 0 的点都加入队列中,直到遇见出口为止。遇到出口时,pos 的 father 路径就是最短路径的逆路径。此时只需要把逆路径反转一下即可。/
//DFS深度优先搜索
import java.util.*;
// 题目已经提示了 【迷宫只有一条通道】,则直接使用 DFS 找路径就行了,如不有多条路径找最短考虑使用 BFS
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int n = in.nextInt();
int m = in.nextInt();
// 构造迷宫
int[][] map=new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j]=in.nextInt();
}
}
//路径存储的数组,Pos
List<Pos> path=new ArrayList<>();
// DFS 搜索路径
dfs(map,0,0,path);
// 输出
for (Pos p : path) {
System.out.println("("+p.x+","+p.y+")");
}
}
}
// 返回值 标记是否找到可通行的路劲
private static boolean dfs(int[][] map,int x,int y,List<Pos> path){
// 添加路径并标记已走
path.add(new Pos(x,y));
map[x][y]=1;
// 结束标志
if(x== map.length-1 && y==map[0].length-1){
return true;
}
// 向下能走时
if (x+1< map.length&&map[x+1][y]==0){
if(dfs(map,x+1,y,path)){
return true;
}
}
// 向右能走时
if (y+1< map[0].length&&map[x][y+1]==0){
if(dfs(map,x,y+1,path)){
return true;
}
}
// 向上能走时
if (x-1>-1&&map[x-1][y]==0){
if(dfs(map,x-1,y,path)){
return true;
}
}
// 向左能走时
if (y-1>-1&&map[x][y-1]==0){
if(dfs(map,x,y-1,path)){
return true;
}
}
// 回溯
path.remove(path.size()-1);//删除最后一个
map[x][y]=0;
return false;
}
// 简单的位置类
public static class Pos{
int x;
int y;
public Pos(int x,int y){
this.x=x;
this.y=y;
}
}
}
//BFS广度优先搜索
import java.util.*;
// 简单的位置类
class Point{
int px;
int py;
Point father;
Point(int px,int py,Point father){
this.px=px;
this.py=py;
this.father = father;
}
Point(){}
}
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int row = in.nextInt();
int col = in.nextInt();
// 构造迷宫
int[][] grid=new int[row][col];
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
grid[i][j]=in.nextInt();
}
}
Queue<Point> que = new LinkedList<Point>();
que.offer(new Point(0,0,null));
grid[0][0]=1;
Point pos = null;
while(true){
pos = que.poll();
int px = pos.px;
int py = pos.py;
if(px==row-1&&py==col-1)break;//结束标志
else {
// 向下能走时
if(px+1<row&&grid[px+1][py]==0){
grid[px+1][py]=1;
que.offer(new Point(px+1,py,pos));
}
// 向左能走时
if(py-1>=0&&grid[px][py-1]==0){
grid[px][py-1]=1;
que.offer(new Point(px,py-1,pos));
}
// 向上能走
if(px-1>=0&&grid[px-1][py]==0){
grid[px-1][py]=1;
que.offer(new Point(px-1,py,pos));
}
// 向右能走时
if(py+1<col&&grid[px][py+1]==0){
grid[px][py+1]=1;
que.offer(new Point(px,py+1,pos));
}
}
}
// 输出:反转路径
//每个Point都带着她是如何来到这个位置的,这个过程需要回溯才能找到她的出发点
//Stack是栈,栈是后进先出,栈是出入从同一个位置
Stack<Point> stack = new Stack<Point>();
while(pos!=null){
stack.push(pos);
pos=pos.father;
}
while(!stack.isEmpty()){
Point temp = stack.peek();
stack.pop();
System.out.println("("+temp.px+","+temp.py+")");
}
}
}
}
//BFS广度优先搜索-优化:利用递归可以后序输出链表的特性,可以省去反转路径的操作
import java.util.*;
// 简单的位置类
class Point{
int px;
int py;
Point father;
Point(int px,int py,Point father){
this.px=px;
this.py=py;
this.father = father;
}
Point(){}
}
public class Main {
// 利用递归可以后序输出链表的特性,可以省去反转路径的操作:
public static void print(Point p){
if(p != null){
print(p.father);
System.out.println("("+p.px+","+p.py+")");
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int row = in.nextInt();
int col = in.nextInt();
// 构造迷宫
int[][] grid=new int[row][col];
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
grid[i][j]=in.nextInt();
}
}
//队列,用来存放下一步要走的位置,是先进先出
Queue<Point> que = new LinkedList<Point>();
//queue.offer():将指定的元素插入队列。如果任务成功,则offer()返回true,否则返回false。
que.offer(new Point(0,0,null));
grid[0][0]=1;
Point pos = null;
while(true){//同时走当前位置所有能走的方向
pos = que.poll();//queue.poll():返回并删除队列的开头。 如果队列为空,则返回null。
int px = pos.px;
int py = pos.py;
if(px==row-1&&py==col-1){//结束标志
break;
}else {
// 向下走时
if(px+1<row&&grid[px+1][py]==0){
grid[px+1][py]=1;
que.offer(new Point(px+1,py,pos));
}
// 向左走时
if(py-1>=0&&grid[px][py-1]==0){
grid[px][py-1]=1;
que.offer(new Point(px,py-1,pos));
}
// 向上走时
if(px-1>=0&&grid[px-1][py]==0){
grid[px-1][py]=1;
que.offer(new Point(px-1,py,pos));
}
// 向右走时
if(py+1<col&&grid[px][py+1]==0){
grid[px][py+1]=1;
que.offer(new Point(px,py+1,pos));
}
}
}
// 输出:
//每个Point都带着她是如何来到这个位置的,这个过程需要回溯才能找到她的出发点
//,这也是为啥用递归的方式打印终点位置的point的原因
print(pos);
}
}
}
模板
- 题目
- 知识点:
- 菜鸟随记:
算法提示:(填空)
-
本文作者:PickUpShells
本文链接:https://www.cnblogs.com/PickUpShells/p/16782150.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步