牛客-字节刷题

前言:别放弃

入门

ZJ18 万万没想到之聪明的编辑

万万没想到之聪明的编辑

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();
            for (int i = 0; i < n; i++) {
                String next = sc.next();
                StringBuilder sb = new StringBuilder(next);
                int j = 0; // 初始化起始位置
                while(j < sb.length()){
                    int end = j+1; // 设置当前字母后不连续字母位置
                    while(end < sb.length() && sb.charAt(j) == sb.charAt(end)){
                        // 若出现第一个字符相等则后移
                        end++;
                    }
                    if(end - j > 2){
                        // 连续出现3个同样字母以上,删到只剩两个
                        sb.delete(j+2, end);
                        end = j+2;
                    }
                    if(end - j == 2 && j >= 2 && sb.charAt(j-1) == sb.charAt(j-2)){
                        // 出现2个同样字母,检测若是AABB,删除后一个
                        sb.deleteCharAt(j+1);
                    }
                    j++;
                }
                System.out.println(sb.toString());
            }
        }
    }
}

ZJ23 找零

找零

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] coins = new int[]{1, 4, 16, 64};
        final int total = 1024;
        while (sc.hasNext()) {
            int N = sc.nextInt();
            int n = total - N;

            int[] dp = new int[n + 1]; // dp[j]表示凑成j元的最小硬币数,一维数组好的情况下用时稍微少一点,而二维数组就固定死了
            for (int j = 1; j <= n; j++) {
                dp[j] = n + 1; // 没取硬币想凑成j元是不可能的,且题目要找最小硬币数,设置一个合适值n+1就可以
            }
            for (int j = 1; j <= n; j++) {
                for (int coin : coins) {
                    if (j >= coin) {
                    // 一维数组不取值进行比较就默认使用上一层的值
                        dp[j] = Math.min(dp[j], dp[j - coin] + 1);
                    }
                }
            }
            System.out.println(dp[n]);

/*            int[][] dp = new int[coins.length + 1][n + 1]; // dp[i][j]表示前i个硬币凑成j元的最小硬币数
            for (int j = 1; j <= n; j++) {
                dp[0][j] = n + 1;
            }
            // 先容积后取包
            for (int j = 1; j <= n; j++) {
                for (int i = 1; i <= coins.length; i++) {
                    if (j >= coins[i - 1]) {
                        dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
                    } else {
                        dp[i][j] = dp[i - 1][j];
                    }
                }
            }

            // 先取包后容积
            for (int i = 1; i <= coins.length; i++) {
                for (int j = 1; j <= n; j++) { // 这里的j一定到从1开始取
                    if (j >= coins[i - 1]) {
                        dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
                    } else {
                        dp[i][j] = dp[i - 1][j];
                    }
                }
            }
            System.out.println(dp[coins.length][n]);*/
        }
    }
}

简单

ZJ16 数列的和

数列的和
注:String.format("%.2f",res) 保留小数位

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 m = sc.nextInt();
            double res = 0.;
            double start = n;
            for (int i = 0; i < m; i++) {
                res += start;
                start = Math.sqrt(start);
            }
            System.out.println(String.format("%.2f", res));
        }
    }
}

ZJ17 水仙花数

水仙花数

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int m = sc.nextInt();
            int n = sc.nextInt();
            StringBuilder sb = new StringBuilder();
            for(int i = m; i <= n; i++){
                String s = String.valueOf(i);
                int sum = 0;
                for(int j = 0; j < s.length(); j++){
                    int tmp = Integer.parseInt(s.substring(j, j + 1));
                    sum += Math.pow(tmp, 3);
                }
                if(i == sum){
                    sb.append(s + " ");
                }
            }
            if(sb.length() == 0){
                System.out.println("no");
            }else{
                String trim = sb.toString().trim(); // 去掉两边的空字符
                System.out.println(trim);
            }
        }
    }
}

ZJ19 万万没想到之抓捕孔连顺

万万没想到之抓捕孔连顺

注:这道题利用了二分查找

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 d = sc.nextInt();
            int[] di = new int[n];
            for (int i = 0; i < n; i++) {
                di[i] = sc.nextInt();
            }
            long res = 0L;
            for(int i = 0; i < n; i++){ // 二分查找出离选定位置i距离不超过D的最远位置
                int target = di[i]+d; // 目标值
                int start = i, end = n-1; // 初始寻找范围
                int pos = 0; // 目标位置
                while(start <= end){
                    int mid = start + (end-start)/2;
                    if(di[mid] <= target){
                        pos = mid;
                        start = mid+1;
                    }else{
                        end = mid-1;
                    }
                }
                int l = pos - i; // 第i位置埋伏,其他l个位置中根据组合选2个
                //System.out.println(i + " " + end + " " + l);
                res = (res + Cnm(l))%99997867;
            }
            System.out.println(res);
        }
    }
    // 找出C(l,2)组合数
    private static long Cnm(long l) {
        if(l < 2) // 小于2无法安放两个位置
            return 0;
        if(l == 2)
            return 1;
        return  l*(l-1)/2;
    }
}

ZJ20 雀魂启动

雀魂启动

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[] state = new int[9], helpArr = new int[9]; // 按顺序存放1-9张牌出现次数
        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < 13; i++) {
            int num = sc.nextInt();
            state[num - 1]++;
        }
        for (int i = 0; i < 9; i++) {
            if (state[i] < 4) {
                // 只要某张牌未出现4次,都可以再添加一张凑成14张牌,判断是否胡牌
                int num = i + 1; // 第3位置即表示牌4
                System.arraycopy(state, 0, helpArr, 0, 9);
                helpArr[i]++; // 该位置添加一张牌
                if (canHu(helpArr, 14, false)) 
                    res.add(num);
            }
        }
        String outString = null;
        if (res.isEmpty()) 
            outString = "";// 若没胡过牌
        else {
            StringBuilder sb = new StringBuilder();
            for(Integer element : res){
                sb.append(element + " ");
            }
            outString = sb.toString().trim();
        }
        System.out.println(outString);
    }
 
    private static boolean canHu(int[] arr, int total, boolean hasHead) {
        if (total == 0) // 若牌按照一个雀头,剩下顺子或刻子的规则取完则算胡牌
            return true;
        if (!hasHead) { // 还未选择两张牌做雀头
            for (int i = 0; i < 9; i++) {
                if (arr[i] >= 2) {
                    arr[i] -= 2; //抽两张牌做雀头
                    if (canHu(arr, total - 2, true)) 
                        return true;
                    // 不能胡牌回溯,重新选择两张牌做雀头,恢复原状态
                    arr[i] += 2;
                }
            }
        }else{
        // 已经选择两张牌做雀头
            for (int i = 0; i < 9; i++) {
                if (arr[i] > 0) {
                    // 只有仍存在牌才有取出顺子或刻子的可能
                    if (arr[i] >= 3) {
                        // 若一张牌出现3次,则取出来做刻子
                        arr[i] -= 3;
                        if (canHu(arr, total - 3, true)) 
                            return true;
                        // 不能胡牌回溯
                        arr[i] += 3;
                    }else if (i + 2 < 9 && arr[i + 1] > 0 && arr[i + 2] > 0) {
                        // 若一张牌没有出现3次,但范围介于1-7间,且找到了后两张牌,可以用来做顺子
                        arr[i]--;
                        arr[i + 1]--;
                        arr[i + 2]--;
                        if (canHu(arr, total - 3, true)) 
                            return true;
                        // 不能胡牌回溯
                        arr[i]++;
                        arr[i + 1]++;
                        arr[i + 2]++;
                    }
                }
            }
        }
        return false;// 遍历完了仍没有胡牌
    }
}

中等

ZJ27 字典序

字典序
题解

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            long n = sc.nextLong();
            long m = sc.nextLong();
            long i = 1;
            m--; // 找第一个结点,那么就是1
            while(m != 0){
                long start = i, end = i+1;
                long num = 0;
                while(start <= n){
                    num += Math.min(end, n+1)-start;
                    start *= 10;
                    end *= 10;
                }
                if(m < num){
                    // 在当前节点下,继续向下查找 ,小于的原因是num就包含当前i结点,而m是当前节点的下一个结点开始数
                    i *= 10;
                    m--;
                }else{
                    // 往右兄弟结点寻找
                    i++;
                    m -= num;
                }
            }
            System.out.println(i);
        }
    }
}

ZJ26 异或

ZJ26 异或
字典树,以后还需了解线段树
字节真题 ZJ26-异或:使用字典树代替暴力破解降低时间复杂度

import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        TrieTree tree = new TrieTree(Integer.toBinaryString((int) 1e5).length());

        while(sc.hasNext()){
            int n = sc.nextInt();
            int m = sc.nextInt();
            long total = 0;
            //插入A1
            tree.insert(sc.nextInt());
            for(int i = 0; i < n - 1; i ++){
                int me = sc.nextInt();
                //找出和Ax异或后结果大于m的数有多少个,并且累加
                total += tree.compare(me, m);
                //插入Ax
                tree.insert(me);
            }
            //输出结果
            System.out.println(total);
        }
    }
    private static class Node{
        //有多少个数有当前前缀
        public int count = 1;
        //子节点
        public Node[] child = new Node[2];
    }

    private static class TrieTree{
        //根节点
        Node root;
        // 总共比较32位,但是最高符号位不用比较,所以最多只需31位,0-30
        int bit;

        public TrieTree(int bit){
            //根节点不包含信息,卫星数据
            this.root = new Node();
            this.bit = bit;
        }

        public int compare(int Ax, int m){
            return compare(root, Ax, m, bit);
        }

        public void insert(int tar){
            insert(tar, bit);
        }

        // now : 当前前缀树中,需要开始比较的节点
        // tar : 将要插入的数,但是在调用insert插入之前,要和已经插入的数比较(当前前缀树)
        // m : 要大于的那个树
        // bit : 当前节点的儿子们表示的是对第几位的比较
        private int compare(Node now, int tar, int m, int bit){
            //逐位比较
            for(int i = bit; i >= 0; i --){
                if(now == null){
                    //空节点 表示两者相同 情况5
                    return 0;
                }
                int res = (tar >>> i) & 1;
                //存在能够 XOR 出 1 的路径, 情况1或2
                if((now.child[res ^ 1]) != null){
                    //如果目标的当前位是 0,说明异或结果已经小于 Ax
                    if(((m >>> i) & 1) == 0){
                        //情况1
                        // 但是不能单纯只返回异或结果大于m的那条路径上的分支数量
                        //因为当前位异或结果相等于m的那条路径上的分支可能还存在满足异或结果大于m的情况
                        return now.child[res ^ 1].count + compare(now.child[res], tar, m, i - 1);
                    } else {
                        //情况2
                        // 异或结果相等 接着找
                        now = now.child[res ^ 1];
                    }
                } else{
                    //情况3
                    //异或结果小于 m, 直接返回0
                    if(((m >>> i) & 1) == 1){
                        return 0;
                    }
                    //情况4
                    // 异或结果相等,接着找
                    now = now.child[res];
                }
            }
            //默认返回0
            return 0;
        }

        private void insert(int tar, int bit){
            Node now = root;
            root.count ++;
            //从第二位开始,忽略最高为第一位,所有输入都是正数,忽略最高符号位
            for(int i = bit; i >= 0; i --){
                //获取要插入的数的第(32 - i )位
                int res = (tar >>> i) & 1;
                //如果之前不存在节点,则新建节点,count 默认 = 1
                if(now.child[res] == null){
                    now.child[res] = new Node();
                    now = now.child[res];
                }else{
                    //如果之前已经有节点存在,则count++
                    now = now.child[res];
                    now.count ++;
                }
            }
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值