头条笔试 20190111

头条笔试 20190111

笔试时长1小时,1道easy,2道middle 平台为newcoder 不允许自带的IDE

第一题 easy

输入k个单词,输出在字典里能找到的单词

字典的格式是二维数组,每个数组里有一个字母,单词的字母之间必须相邻,并且每个字符只能用一次

输入 单词数 k 字典的行列为m,n

输入 单词,字典

例:

3 5 4

help let zoo

heabL
alpte
bfgdd
cedbm

输出

help

let

解题思路

该题很简单,递归即可

如果当前的字符相同,则比较下一个位置的左右下位置是否存在相同的字符,直到所有字符都能找到,或者所有相邻位置都找不到

import java.util.Scanner;

/**
 * @author hxh
 * @date 2019-01-12 13:56
 */
public class BD1 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int k = scanner.nextInt();
        int m = scanner.nextInt();
        int n = scanner.nextInt();
        String[] words = new String[k];
        for (int i = 0; i < k; i++) {
            words[i] = scanner.next();
        }
        char[][] chars = new char[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                chars[i][j] = scanner.next().charAt(0);
            }
        }
        for (int i = 0; i < k; i++) {
            String str = words[i];
            boolean exist = false;
            for (int j = 0; j < m; j++) {
                if (exist) {
                    break;
                }
                for (int z = 0; z < n; z++) {
                    if (search(j, z, str, 0, chars)) {
                        exist = true;
                        System.out.println(str);
                        break;
                    }
                }
            }
        }
    }

    public static boolean search(int i, int j, String str, int index, char[][] chars) {
        if (index >= str.length()) {
            //字符串所有的字符都已经在字典里找到,返回true表示已经找到
            return true;
        }
        //检测边界
        boolean valid = i >= 0 && j >= 0 && i < chars.length && j < chars[0].length;
        if (!valid) {
            //越界
            return false;
        }
        if (chars[i][j] == str.charAt(index)) {
            //当前字符一样
            //依次检查左 右 上 下 的位置是否和字典的下一位相同
            //当前位置已经被占用
            char temp = chars[i][j];
            boolean exist = search(i, j - 1, str, index + 1, chars) ||
                    search(i, j + 1, str, index + 1, chars) ||
                    search(i + 1, j, str, index + 1, chars) ||
                    search(i - 1, j, str, index + 1, chars);
            chars[i][j] = temp;

            return exist;
        }
        return false;

    }
}

第二题 middle

一圆上的十等分点,依次标上0,1,2,3,4,5,6,7,8,9。每一步可以顺时针也可以逆时针走一个点,问经过n步回到原点的路径有多少种。输出结果对1000000007的余数

例如 n = 2时 ,有2种: 0-1-0 和 0-9-0

这题还是很有挑战性的,一开始是想会不会像斐波那契数列一样通过递推,毕竟需要取余。然而没有找到递推式子。就没有做出来放弃了,第二天和室友吃饭时他提到0,1数组,也没有太明白。回到寝室后躺在床上突然一惊,如果是-1,1的数组的话,则他们的和的绝对值应该是10的倍数或者是0(回到圆点)然后心里面大概明白其实是个有重复元素的全排列问题。

首先全排列的公式为n里面有x个元素是重复的

$A_{n}^{x} = \frac{n!}{x!} $

假设1的数量为x,2的数量为y则x和y的关系满足
x+y=n and |x-y| mod 10=0

这时候问题貌似就很简单了,只需要计算出所有满足条件的x,y相加然后取余就可以了。但是既然让取余说明数字会比较大,会出现溢出问题,乘法和加法都很好解决

有公式 (x+y)%n == (x%n+y%n)%nx*y %n = ((x%n)*(y%n)%n);

但是计算公式里涉及到除法,就并没有相应的公式了。迅速百度根据相关数学知识的得到逆元公式

链接: ?链接

整体思想就是化除为乘,a / b mod n = a * b1 mod n b1就是b的逆元

然后就可以找到所有的x,y的排列对1000000007的余数了

代码如下

import java.util.Scanner;

/**
 * @author hxh
 * @date 2019-01-12 13:56
 */
public class BD2 {
    private static final long MOD = 1000000007L;
    public static long[] inv = new long[10000];

    public static void init() {
        inv[1] = 1L;
        for (int i = 2; i < inv.length; i++) {
            //费马小定理 除法运算的取模 逆元
            inv[i] = inv[(int) (MOD % i)] * (MOD - MOD / i) % MOD;
        }
    }

    public static void main(String[] args) {
        init();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long answer = function(n);

        System.out.println(answer);
    }

    public static long function(int n) {
        if (n % 2 != 0) {
            return 0;
        }
        long answer = 0;
        long fn = 1;
        for (int i = 0; i < n; i++) {
            //乘法的取模运算
            fn = (fn % MOD) * ((i + 1) % MOD) % MOD;
        }
        // 当成 -1 和 1看待 x + y的和的绝对值是10的倍数 故是有重复元素的全排列 结果为 n! / (a! * b!);
        for (int i = 0; i < n / 10 + 1; i++) {
            long f = fn;
            long x = (n - i * 10) / 2;
            long y = (n + i * 10) / 2;
            for (int j = 1; j <= x; j++) {
                f = (f * inv[j]) % MOD;
            }
            for (int j = 1; j <= y; j++) {
                f = (f * inv[j]) % MOD;
            }
            if (i != 0) {
                f = (f * 2) % MOD;
            }
            answer = (answer % MOD + f % MOD) % MOD;
        }
        return answer;
    }
}
/**
 * 题目详情
 * 一个圆上一次有0到9共十个点,9和0联通,问经过n步回到0点的路径有多少种
 * 示例 n=4 则有6种
 * n = 2 有1种
 */

第三题 middle

手中有一副牌,根据以下规则放到桌子上面

  1. 把手中的第一张牌放到桌子上
  2. 把手中剩余的第一张牌放到最后
  3. 重复1、2直到所有牌已经放完

现已经知道桌子上牌的顺序,求手中牌的顺序

输入 几轮牌

牌的顺序

例题

输入:

1

4 2 3 1

输出

1 2 3 4

解题思路:

刚开始想的是模拟一下手中牌的放置过程,然后根据放置的结果得到桌子上的每张牌的初始位置。即刚开始用一个链表记录索引,值依次为1,2,3…n然后根据用另一个链表当作桌子,记录牌的放置。最终后一个链表的节点的值表示了该节点之前的位置。然而没有实现。同样是今天在床上,突然有了思路。

假设牌为

1,2,3,4,5,6,7,8,9,10,11,12

注意到

第一次拿到牌为 1,3,5,7,9,11然后牌的顺序为2,4,6,8,10

再一次的顺序为2,6,10剩下的顺序为4,8

然后就4,8

即牌的依次顺序为1 3 5 7 9 11 2 6 10 4 8

拿牌的间隔由2到4再到8

故有了如下代码

import java.util.Scanner;

/**
 * @author hxh
 * @date 2019-01-12 13:56
 */
public class BD3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n =Integer.parseInt(scanner.nextLine());
        for (int i = 0; i < n; i++) {
            String[] strs = scanner.nextLine().split(" ");
            String[] origin = new String[strs.length];
            int count = 0;
            int index = 0;
            int step = 2;
            while (count != origin.length) {
                origin[index] = strs[strs.length - count - 1];
                index += step;
                count++;
                if (index >= origin.length) {
                    index = step - 1;
                    step *= 2;
                }
            }
            System.out.println();
            for (int j = 0; j < origin.length; j++) {
                System.out.print(origin[j] + " ");
            }
            System.out.println();
        }
    }
}
/**
 * 手中一副牌 做如下操作
 * 1. 将手中的第一张牌放到桌面上
 * 2. 将剩余手中的第一张牌放在最后
 * 3. 重复上述过程
 * 4. 现在已经知道在桌面上的牌的顺序 求手中牌的初始顺序
 */
// 分析 其实就是间隔取元素,取完后再从剩下的空当中间隔取元素
// 0 1 2 3 4 5 6 7 8 9 10
// 0   2   4   6   8   10
//   1   3   5   7   9
//   1       5       9
//       3       7
//               7
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值