牛客算法题(第四期)

一、大数乘法(高精度乘法)

大数乘法_牛客题霸_牛客网

解题思路:

  1. 模拟竖式乘法:将两个字符串反转,从低位开始计算,模拟手工乘法过程。反转字符串是为了方便从个位开始计算,符合竖式乘法的计算习惯。

  2. 逐位相乘累加:用数组存储中间结果,tmpi+j 存储第 i 位和第 j 位相乘的结果。这里使用 i+j 作为索引是因为两个数字的第 i 位和第 j 位相乘的结果应该放在结果的第 (i+j) 位上,这是竖式乘法的基本原理。

  3. 处理进位:从低位到高位依次处理进位,确保每位数字在 0-9 范围内。遍历 tmp 数组,将当前位的值加上进位,然后取模 10 得到该位的最终数字,整除 10 得到新的进位。

  4. 处理剩余进位:在完成所有位的进位处理后,如果还有进位,需要继续添加到结果中,直到进位为 0。

  5. 去除前导零:反转结果前,去除高位多余的零(但保留最后一位零)。注意要保留至少一位数字,因为 0 乘以任何数都是 0,结果应该是 "0" 而不是空字符串。

  6. 反转并返回结果:将处理好的字符数组反转,得到最终的正确顺序的字符串结果。

代码实现:

复制代码
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();
    }
}

二、最小公倍数

求最小公倍数_牛客题霸_牛客网

解题思路:

  1. 理解最小公倍数定义:最小公倍数(LCM)是指能被两个或多个整数整除的最小正整数。对于两个数 a 和 b,它们的最小公倍数可以通过最大公约数(GCD)来计算。

  2. 公式法原理:利用数学公式:最小公倍数 = 两数乘积 / 最大公约数。即 LCM(a, b) = a × b / GCD(a, b)。这个公式基于数论中的基本定理。

  3. 辗转相除法求最大公约数:使用递归或迭代的辗转相除法(欧几里得算法)计算最大公约数。算法原理:GCD(a, b) = GCD(b, a mod b),直到 b 为 0 时,a 就是最大公约数。

  4. 边界情况处理:注意输入可能为 0 的情况。根据定义,0 和任何数的最大公约数是另一个数,而 0 和任何数的最小公倍数是 0。在代码实现中需要正确处理这些边界情况。

  5. 计算与输出:根据公式计算最小公倍数并输出结果。注意整数溢出的问题,如果 a 和 b 很大,可以先计算 GCD,然后用 a / GCD × b 的方式计算 LCM,避免中间结果溢出。

  6. 算法复杂度分析:辗转相除法的时间复杂度为 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));
    }
}

三、在字符串中找出连续最长的字符串(模拟+双指针)

字符串中找出连续最长的数字串_牛客题霸_牛客网

解题思路:

  1. 问题分析:需要在字符串中找到最长的连续数字子串。数字字符包括 '0'-'9',连续数字子串是指由数字字符组成的、中间没有其他字符的连续序列。

  2. 双指针扫描策略:遍历字符串,使用双指针 i 和 j 定位连续数字串的起止位置。i 指向当前扫描的起始位置,j 从 i 开始向后移动,直到遇到非数字字符或字符串结尾。

  3. 识别数字字符:当遇到数字字符时,移动 j 指针直到遇到非数字字符或字符串结尾。使用 while 循环连续移动 j,直到 sj 不是数字字符或 j 到达字符串末尾。

  4. 更新最长串记录:比较当前数字串长度 (j-i) 与已记录的最大长度,如果更长,则更新最长串的起始位置 begin 和长度 len。注意要记录的是起始位置,而不是结束位置。

  5. 跳过已处理部分:将 i 指针移动到 j 的位置,避免重复扫描。因为从 i 到 j-1 的字符已经处理完毕,下一次应该从 j 开始继续扫描。

  6. 输出结果:根据记录的起始位置 begin 和长度 len,使用循环输出从 begin 到 begin+len-1 的字符。如果字符串中没有数字字符,应该输出空字符串。

  7. 时间复杂度分析:每个字符最多被访问两次(一次作为 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)

拼三角

解题思路:

  1. 问题理解:有六根木棍,需要判断是否能将它们分成两组,每组三根,且每组都能构成三角形。三角形需要满足任意两边之和大于第三边。

  2. 排序预处理:将六根木棍长度排序,便于组合判断。排序后可以更容易地应用三角形判定条件和尝试合理的分组。

  3. 三角形判定条件:对于三根木棍 a ≤ b ≤ c,能构成三角形的充要条件是 a + b > c。这是三角形不等式的最简形式,排序后只需要检查这一条件。

  4. 分组策略分析:由于只有六根木棍,可以枚举所有可能的分组方式。但更高效的方法是尝试几种典型的分组组合,因为排序后最优的分组往往是将木棍按大小交错分配。

  5. 常见分组模式检查:检查几种典型的分组组合:

  • 模式1:最小的两根与第三小一组,剩余三根一组
  • 模式2:最小的与第三小、第四小一组,剩余三根一组
  • 模式3:最小的与第三小、第五小一组,剩余三根一组
  • 模式4:最小的与第四小、第五小一组,剩余三根一组

这四种模式覆盖了大多数合理分组情况。

  1. 输出结果:如果存在一种分组使得两个三角形都能构成,输出 "Yes",否则输出 "No"。注意题目可能有多个测试用例,需要循环处理。

  2. 算法优化:由于木棍数量固定为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),但元素在原数组中的顺序可以是任意的。

  2. 排序预处理:对数组进行排序,使连续元素相邻。排序后,连续的子序列在排序后的数组中会形成连续的片段,便于统计。

  3. 遍历统计策略:遍历排序后的数组,统计连续递增(差值为1)的子序列长度。使用双指针或单指针配合 while 循环来统计连续段的长度。

  4. 处理重复值:遇到重复元素时跳过,不影响连续性的判断。重复元素不算作连续序列的一部分,但也不应该中断连续性判断。

  5. 更新最大长度:在连续性中断时(当前元素与前一个元素的差值大于1),更新记录的最大连续子序列长度,然后开始统计新的连续段。

  6. 指针跳跃优化:使用外层 for 循环和内层 while 循环的组合。当内层 while 循环结束时,将外层循环的 i 直接跳到 j 的位置,避免重复遍历。

  7. 边界情况处理:空数组返回0,单个元素的数组返回1。注意数组可能包含负数,但排序和连续性判断不受影响。

  8. 时间复杂度分析:排序时间复杂度为 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;
    }
}

六、添加逗号(模拟)

解题思路:

  1. 从右向左处理:由于逗号是从右向左每三位添加一个,所以遍历时从字符串末尾开始考虑更方便。

  2. 正向遍历与条件判断:实际代码中常采用正向遍历,通过计算当前位置距离末尾的距离来判断是否需要添加逗号。

  3. 添加逗号的条件:当剩余字符数(n-i-1)是3的倍数且不是最后一个字符时,在当前字符后添加逗号。

  4. 逐字符输出:遍历字符串,输出每个字符,在满足条件的位置输出逗号。

代码实现:

复制代码
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(',');
        }
       }
    }
}

七、跳台阶(动态规划)

跳台阶_牛客题霸_牛客网

解题思路:

  1. 动态规划定义:设 dpi 表示跳上第 i 级台阶的跳法总数。

  2. 状态转移方程:由于每次可以跳 1 级或 2 级,所以 dpi = dpi-1 + dpi-2

  3. 初始条件:dp0 = 1(不跳也算一种方式),dp1 = 1(只能跳 1 级)。

  4. 空间优化:由于 dpi 只依赖于前两个状态,可以用三个变量 a、b、c 滚动更新,将空间复杂度优化到 O(1)。

  5. 边界处理:当 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);
        }
    }
}

八、小结

以为六点下班回来还能学会习呢,但其实回来已经累死了,等我调整下状态,在持续给大家更新。想吃榴莲但是和别人合租,吃不完的不敢放冰箱,怕人家嫌味大,但是我真的好想吃啊

相关推荐
雪碧聊技术1 小时前
力扣 回溯法 | LCR 020. 回文子串
javascript·算法·leetcode
wabs6661 小时前
关于哈希表【力扣454.四数相加II的思考】
数据结构·算法·leetcode·散列表
我能坚持多久1 小时前
优选算法——专题一双指针(上):附四道例题详解
c++·学习·算法
Tim_101 小时前
【C++】023、移动语义&深拷贝
开发语言·c++·算法
月光船幽幽2 小时前
锁死后干预有效性的关键突破
人工智能·python·算法
2501_906565122 小时前
哥德巴赫猜想
算法
ShineWinsu2 小时前
对于C++中unordered_map的详细介绍
数据结构·c++·算法·面试·stl·哈希表·unordered_map
东东最爱敲键盘2 小时前
day3.c++函数与预处理 string
c++·算法
七夜zippoe2 小时前
基于 DolphinDB 构建全流程质量追溯体系:从原材料到成品的全链路追踪
大数据·人工智能·算法·全链路·dolphindb·质量追溯