头条笔试 20190111
笔试时长1小时,1道easy,2道middle 平台为newcoder 不允许自带的IDE
第一题 easy
输入k个单词,输出在字典里能找到的单词
字典的格式是二维数组,每个数组里有一个字母,单词的字母之间必须相邻,并且每个字符只能用一次
输入 单词数 k 字典的行列为m,n
输入 单词,字典
例:
3 5 4
help let zoo
h | e | a | b | L |
---|---|---|---|---|
a | l | p | t | e |
b | f | g | d | d |
c | e | d | b | m |
输出
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)%n
和x*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直到所有牌已经放完
现已经知道桌子上牌的顺序,求手中牌的顺序
输入 几轮牌
牌的顺序
例题
输入:
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