一、大数乘法(高精度乘法)
解题思路:
模拟竖式乘法:将两个字符串反转,从低位开始计算,模拟手工乘法过程。反转字符串是为了方便从个位开始计算,符合竖式乘法的计算习惯。
逐位相乘累加:用数组存储中间结果,tmpi+j 存储第 i 位和第 j 位相乘的结果。这里使用 i+j 作为索引是因为两个数字的第 i 位和第 j 位相乘的结果应该放在结果的第 (i+j) 位上,这是竖式乘法的基本原理。
处理进位:从低位到高位依次处理进位,确保每位数字在 0-9 范围内。遍历 tmp 数组,将当前位的值加上进位,然后取模 10 得到该位的最终数字,整除 10 得到新的进位。
处理剩余进位:在完成所有位的进位处理后,如果还有进位,需要继续添加到结果中,直到进位为 0。
去除前导零:反转结果前,去除高位多余的零(但保留最后一位零)。注意要保留至少一位数字,因为 0 乘以任何数都是 0,结果应该是 "0" 而不是空字符串。
反转并返回结果:将处理好的字符数组反转,得到最终的正确顺序的字符串结果。
代码实现:
import java.util.*;
public class Solution {
public String solve (String s, String t) {
char[] ss=new StringBuffer(s).reverse().toString().toCharArray();
char[] tt=new StringBuffer(t).reverse().toString().toCharArray();
int m=ss.length,n=tt.length;
int[] tmp=new int[m+n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
tmp[i+j]+=(ss[i]-'0')*(tt[j]-'0');
}
}
StringBuffer ret=new StringBuffer();
int c=0;
for(int x:tmp){
c+=x;
ret.append((char)(c%10+'0'));
c/=10;
}
while(c!=0){
ret.append((char)(c%10+'0'));
c/=10;
}
while(ret.length()>1 && ret.charAt(ret.length()-1) == '0'){
ret.deleteCharAt(ret.length()-1);
}
return ret.reverse().toString();
}
}
二、最小公倍数
解题思路:
理解最小公倍数定义:最小公倍数(LCM)是指能被两个或多个整数整除的最小正整数。对于两个数 a 和 b,它们的最小公倍数可以通过最大公约数(GCD)来计算。
公式法原理:利用数学公式:最小公倍数 = 两数乘积 / 最大公约数。即 LCM(a, b) = a × b / GCD(a, b)。这个公式基于数论中的基本定理。
辗转相除法求最大公约数:使用递归或迭代的辗转相除法(欧几里得算法)计算最大公约数。算法原理:GCD(a, b) = GCD(b, a mod b),直到 b 为 0 时,a 就是最大公约数。
边界情况处理:注意输入可能为 0 的情况。根据定义,0 和任何数的最大公约数是另一个数,而 0 和任何数的最小公倍数是 0。在代码实现中需要正确处理这些边界情况。
计算与输出:根据公式计算最小公倍数并输出结果。注意整数溢出的问题,如果 a 和 b 很大,可以先计算 GCD,然后用 a / GCD × b 的方式计算 LCM,避免中间结果溢出。
算法复杂度分析:辗转相除法的时间复杂度为 O(log(min(a, b))),空间复杂度为 O(1)(迭代版本)或 O(log(min(a, b)))(递归版本)。
代码实现;
import java.util.Scanner;
//最小公倍数=a*b/最大公约数
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static int lcm(int a,int b){
if(b==0){
return a;
}
return lcm(b,a%b);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int a=in.nextInt(),b=in.nextInt();
System.out.print(a*b/lcm(a,b));
}
}
三、在字符串中找出连续最长的字符串(模拟+双指针)
解题思路:
问题分析:需要在字符串中找到最长的连续数字子串。数字字符包括 '0'-'9',连续数字子串是指由数字字符组成的、中间没有其他字符的连续序列。
双指针扫描策略:遍历字符串,使用双指针 i 和 j 定位连续数字串的起止位置。i 指向当前扫描的起始位置,j 从 i 开始向后移动,直到遇到非数字字符或字符串结尾。
识别数字字符:当遇到数字字符时,移动 j 指针直到遇到非数字字符或字符串结尾。使用 while 循环连续移动 j,直到 sj 不是数字字符或 j 到达字符串末尾。
更新最长串记录:比较当前数字串长度 (j-i) 与已记录的最大长度,如果更长,则更新最长串的起始位置 begin 和长度 len。注意要记录的是起始位置,而不是结束位置。
跳过已处理部分:将 i 指针移动到 j 的位置,避免重复扫描。因为从 i 到 j-1 的字符已经处理完毕,下一次应该从 j 开始继续扫描。
输出结果:根据记录的起始位置 begin 和长度 len,使用循环输出从 begin 到 begin+len-1 的字符。如果字符串中没有数字字符,应该输出空字符串。
时间复杂度分析:每个字符最多被访问两次(一次作为 i,一次作为 j 的移动),因此时间复杂度为 O(n),其中 n 是字符串长度。
代码实现:
import java.util.Scanner;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) throws Exception{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
char[] s=br.readLine().toCharArray();
int begin=0,len=0;
for(int i=0;i<s.length;i++){
if(s[i] >= '0' && s[i] <= '9'){
int j=i;
while(j<s.length && s[j]>='0'&&s[j]<='9' && j<s.length){
j++;
}
if(j-i>len){
begin=i;
len=j-i;
}
i=j;
}
}
for(int i=begin;i<begin+len;i++){
System.out.print(s[i]);
}
}
}
四、拼三角(枚举/dfs)
解题思路:
问题理解:有六根木棍,需要判断是否能将它们分成两组,每组三根,且每组都能构成三角形。三角形需要满足任意两边之和大于第三边。
排序预处理:将六根木棍长度排序,便于组合判断。排序后可以更容易地应用三角形判定条件和尝试合理的分组。
三角形判定条件:对于三根木棍 a ≤ b ≤ c,能构成三角形的充要条件是 a + b > c。这是三角形不等式的最简形式,排序后只需要检查这一条件。
分组策略分析:由于只有六根木棍,可以枚举所有可能的分组方式。但更高效的方法是尝试几种典型的分组组合,因为排序后最优的分组往往是将木棍按大小交错分配。
常见分组模式检查:检查几种典型的分组组合:
- 模式1:最小的两根与第三小一组,剩余三根一组
- 模式2:最小的与第三小、第四小一组,剩余三根一组
- 模式3:最小的与第三小、第五小一组,剩余三根一组
- 模式4:最小的与第四小、第五小一组,剩余三根一组
这四种模式覆盖了大多数合理分组情况。
输出结果:如果存在一种分组使得两个三角形都能构成,输出 "Yes",否则输出 "No"。注意题目可能有多个测试用例,需要循环处理。
算法优化:由于木棍数量固定为6,可以尝试所有 C(6,3)/2 = 10 种不同的分组方式,但上述四种模式在大多数情况下已经足够。
代码实现:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int t=in.nextInt();
int[] arr=new int[6];
while(t-- != 0){
for(int i=0; i<6 ;i++){
arr[i]=in.nextInt();
}
Arrays.sort(arr);
if(arr[0]+arr[1]>arr[2] && arr[3]+arr[4]>arr[5]||
arr[0]+arr[2]>arr[3] && arr[1]+arr[4]>arr[5]||
arr[0]+arr[3]>arr[4] && arr[1]+arr[2]>arr[5]||
arr[0]+arr[4]>arr[5] && arr[1]+arr[2]>arr[3]
){
System.out.println("Yes");
}else{
System.out.println("No");
}
}
}
}
五、数组中最长的连续子序列(排序+模拟)
解题思路:
问题定义:在无序整数数组中找出最长的连续子序列的长度。连续子序列要求序列中的元素值连续递增(差值为1),但元素在原数组中的顺序可以是任意的。
排序预处理:对数组进行排序,使连续元素相邻。排序后,连续的子序列在排序后的数组中会形成连续的片段,便于统计。
遍历统计策略:遍历排序后的数组,统计连续递增(差值为1)的子序列长度。使用双指针或单指针配合 while 循环来统计连续段的长度。
处理重复值:遇到重复元素时跳过,不影响连续性的判断。重复元素不算作连续序列的一部分,但也不应该中断连续性判断。
更新最大长度:在连续性中断时(当前元素与前一个元素的差值大于1),更新记录的最大连续子序列长度,然后开始统计新的连续段。
指针跳跃优化:使用外层 for 循环和内层 while 循环的组合。当内层 while 循环结束时,将外层循环的 i 直接跳到 j 的位置,避免重复遍历。
边界情况处理:空数组返回0,单个元素的数组返回1。注意数组可能包含负数,但排序和连续性判断不受影响。
时间复杂度分析:排序时间复杂度为 O(n log n),遍历统计时间复杂度为 O(n),总体时间复杂度为 O(n log n)。空间复杂度为 O(1)(如果不考虑排序的额外空间)。
代码实现:
import java.util.*;
public class Solution {
public int MLS (int[] arr) {
Arrays.sort(arr);
int n=arr.length,ret=0;
for(int i=0;i<n;){
int j=i+1,count=1;
while(j<n){
if(arr[j]-arr[j-1]==1){
j++;
count++;
}else if(arr[j]-arr[j-1]==0){
j++;
}else{
break;
}
}
ret=Math.max(ret,count);
i=j;
}
return ret;
}
}
六、添加逗号(模拟)
解题思路:
从右向左处理:由于逗号是从右向左每三位添加一个,所以遍历时从字符串末尾开始考虑更方便。
正向遍历与条件判断:实际代码中常采用正向遍历,通过计算当前位置距离末尾的距离来判断是否需要添加逗号。
添加逗号的条件:当剩余字符数(n-i-1)是3的倍数且不是最后一个字符时,在当前字符后添加逗号。
逐字符输出:遍历字符串,输出每个字符,在满足条件的位置输出逗号。
代码实现:
import java.util.Scanner;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader rd=new BufferedReader(new InputStreamReader(System.in));
String s=rd.readLine();
int n=s.length();
for(int i=0;i<n;i++){
System.out.print(s.charAt(i));
if((n-i-1)%3==0 && i!=n-1){
System.out.print(',');
}
}
}
}
七、跳台阶(动态规划)
解题思路:
动态规划定义:设 dpi 表示跳上第 i 级台阶的跳法总数。
状态转移方程:由于每次可以跳 1 级或 2 级,所以 dpi = dpi-1 + dpi-2。
初始条件:dp0 = 1(不跳也算一种方式),dp1 = 1(只能跳 1 级)。
空间优化:由于 dpi 只依赖于前两个状态,可以用三个变量 a、b、c 滚动更新,将空间复杂度优化到 O(1)。
边界处理:当 n=0 或 n=1 时直接返回 n,避免数组越界。
代码实现:
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 a=1,b=1,c=0;
for(int i=2;i<=n;i++){
c=a+b;
a=b;
b=c;
}
if(n==0||n==1){
System.out.println(n);
}else{
System.out.println(c);
}
}
}
八、小结
以为六点下班回来还能学会习呢,但其实回来已经累死了,等我调整下状态,在持续给大家更新。想吃榴莲但是和别人合租,吃不完的不敢放冰箱,怕人家嫌味大,但是我真的好想吃啊