校招中我,细细品味了这些题... | 掘金技术征文

本人北京某高校计算机硕士,总以为本科毕业仍是不久前的事情,转眼间研究生也要毕业了,7月开始投入校招大军,到目前为止也积攒了一些面试笔试题以及我的的学习总结,如今分享一波,但愿能对你们有所帮助。skr~~😊php

今日头条后端方向笔试题

第一题:字符交换

题目

字符串S由小写字母构成,长度为n。定义一种操做,每次均可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次以后,字符串中最多有多少个连续的位置上的字母相同?
输入描述:java

第一行为一个字符串S与一个非负整数m。(1 <= |S| <= 1000, 1 <= m <= 1000000)面试

输出描述:express

一个非负整数,表示操做以后,连续最长的相同字母数量。编程

输入例子1:后端

abcbaa 2数组

输出例子1:bash

2app

例子说明1:
使2个字母a连续出现,至少须要3次操做。即把第1个位置上的a移动到第4个位置。 因此在至多操做2次的状况下,最多只能使2个b或2个a连续出现。less

思路

动态规划。 以a字符为例,令 dp[i][j] 表示将从第 i 个 a 字符(包含)到第 j 个 a 字符(包含)之间的全部 a 字符移动到一块儿的交换次数,咱们能够知道将全部的字符往中间移动的代价是最小的。
同时,假设从第 i + 1 个 a 字符到第 j - 1 个 a 字符之间的全部字符 a 都已经移动到一块儿了,不管它们的位置如何,则只需把 i 位置和 j 位置的 a 字符忘中间移动,便可获得把第 i 个 a 字符(包含)到第 之间的全部 到一块儿的最小操做次数,且该步骤的操做次数必定为第 j 个 a 字符的下标减去第 i 个 a 字符的下标加一再减第 i + 1 个 a 字符到第 j - 1 个 a 字符之间的全部字符 a 的数量。

动态转移方程为:

dp[i][j] = dp[i + 1][j] + (index[j] – indexAfterMove[j – 1] – 1) + (indexAfterMove[i + 1] – index[i] – 1) = dp[i + 1][j] + (index[j] – index[i]) – (indexAfterMove[j – 1] – indexAfterMove[i + 1]) – 2 = dp[i + 1][j] + (index[j] – index[i]) – len(i + 1, j – 1) – 1

代码

package exam.q3;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import static java.lang.Math.max;
 
public class Main {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		String str = sc.next();
		int m = sc.nextInt();
		
		int len = str.length();
		HashMap<Character, ArrayList<Integer>> map = new HashMap<>();
		
		for(int i = 0 ; i < len; i ++) {
			ArrayList<Integer> index = map.getOrDefault(str.charAt(i), new ArrayList<>());
			index.add(i);
			map.put(str.charAt(i), index);
		}
	
		int ans = 0;
		for(char k: map.keySet()){
			ArrayList<Integer> index = map.get(k);
			int lenOfIndex = index.size();
			int[][] dp = new int[lenOfIndex][lenOfIndex];
			for(int i = 0; i < lenOfIndex - 1; i++) {
				dp[i][i + 1] =  index.get(i + 1) - 1 - index.get(i);
			}
			for(int num = 2; num <= lenOfIndex; num++) {
				for(int i = 0; i < lenOfIndex - num + 1; i++) {
					dp[i][i + num - 1] = dp[i + 1][i + num - 2]  + index.get(i  + num - 1) - index.get(i) + 1 - num;
					if(dp[i][i + num - 1] <= m) {
						//System.out.println(k + " " + index.get(i) + " " + index.get(i + num - 1) + " " + num + " " + dp[i][i + num - 1]);
						ans = max(ans, num);
					}
				}
			}
		}
		System.out.println(ans);
		
		sc.close();
	}
 
}
复制代码

第二题:二阶魔方

题目

二阶魔方又叫小魔方,是 2*2*2 的立方形结构。每一面都有 4 个块,共有 24 个块。每次操做能够将任意一面逆时针或者顺时针旋转 90°,如将上面逆时针旋转 90° 操做以下。

Nero 在小魔方上作了一些改动,用数字替换每一个块上面的颜色,称之为数字魔方。魔方上每一面的优美度就是这个面上 4 个数字的乘积,而魔方的总优美度就是 6 个面优美度总和。 如今 Nero 有一个数字魔方,他想知道这个魔方在操做不超过 5 次的前提下能达到的最大优美度是多少。 魔方展开后每一块的序号以下图:
输入描述:
输入一行包含 24 个数字,按序号顺序给出魔方每一块上面的数字。全部数大小范围为[-100,100]。
输入描述:
输出一行包含一个数字,表示最大优美度。

输入例子1:

2 -3 -2 3 7 -6 -6 -7 9 -5 -9 -3 -2 1 4 -9 -1 -10 -5 -5 -10 -4 8 2

输出例子1:

8281

思路

原本想用类来抽象魔方的每一个面,而后模拟魔方的旋转,可是太复杂了,不只要存储每一个面的上下左右,并且每一个面的每一个格子也要抽象出来,由于不一样的格子相邻的格子是不同的,旋转的时候要改变其相邻格子。

后来查了一下题解,发现一种很巧妙的方法,用置换群的方法。

每次旋转,只要旋转方式同样,固定位置的格子必定会被替换到另外一固定位置,只要将种旋转方式用置换群抽想出来就能够了,一共6种旋转方式,垂直上下旋转,水平左右旋转和垂直左右旋转。

代码

package exam1.q2;

import java.util.Scanner;

public class Main {

	private static int[][] subs = {
			{0, 1, 11 ,5, 4, 16, 12, 6, 2, 9, 10, 17, 13, 7, 3, 15, 14, 8, 18, 19, 20, 21, 22, 23},
			{0, 1, 8, 14, 4, 3, 7, 13, 17, 9, 10, 2, 6, 12, 16, 15, 5, 11, 18, 19, 20, 21, 22, 23},
			{0, 7, 2, 13, 4, 5, 6, 17, 14, 8, 10, 11, 12, 19, 15, 9, 16, 21, 18, 23, 20, 1, 22, 3},
			{0, 21, 2, 23, 4, 5, 6, 1, 9, 15, 10, 11, 12, 3, 8, 14, 16, 7, 18, 13, 20, 17, 22, 19},
			{2, 0, 3, 1, 6, 7, 8, 9, 23, 22, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 5, 4},
			{1, 3, 0, 2, 23, 22, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 9, 8}
	};
	private static int[][] faces = {
		    {0, 1, 2, 3},
		    {4, 5, 10, 11},
		    {6, 7, 12, 13},
		    {8, 9, 14, 15},
		    {16, 17, 18, 19},
		    {20, 21, 22, 23}
		};
	
	private static long ans;
	private static void substitute(int[] cube, int step) {
		long perf = perfect(cube);
		if(perf > ans)
			ans = perf;
		if(step == 5 ) {
			return;
		}
		for(int i = 0; i < 6; i ++) {
			substitute(rotate(cube, subs[i]), step + 1);
		}
	}
	
	private static int[] rotate(int[] cube, int sub[]) {
		int[] rotated = new int[24];
		
		for(int i = 0; i < 24; i++) {
			rotated[i] = cube[sub[i]];
		}
		
		return rotated;
	}
	
	private static long perfect(int[] cube){
		long perf = 0;
		for(int[] f: faces){
			long t = 1;
			for(int n: f) {
				t *= cube[n];
			}
			perf += t;
		}
		return perf;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		
		int total = 24;
		int[] cube = new int[24];
		for(int i = 0; i < total; i++) {
			cube[i] = sc.nextInt();
		}
		substitute(cube, 0);
		System.out.println(ans);
		sc.close();
	}

}
复制代码

第三题:推箱子

题目

有一个推箱子的游戏, 一开始的状况以下图:

上图中, ‘.’ 表示可到达的位置, ‘#’ 表示不可到达的位置,其中 S 表示你起始的位置, 0表示初始箱子的位置, E表示预期箱子的位置,你能够走到箱子的上下左右任意一侧, 将箱子向另外一侧推进。以下图将箱子向右推进一格;

..S0.. -> …S0.

注意不能将箱子推进到’#’上, 也不能将箱子推出边界;

如今, 给你游戏的初始样子, 你须要输出最少几步可以完成游戏, 若是不能完成, 则输出-1。
输入描述
第一行为2个数字,n, m, 表示游戏盘面大小有n 行m 列(5< n, m < 50); 后面为n行字符串,每行字符串有m字符, 表示游戏盘面;
输出描述
一个数字,表示最少几步能完成游戏,若是不能,输出-1;

输入例子1:

3 6
.S#..E
.#.0..
……

输出例子1:

11

思路

简单四维BFS,注意要同时记录人的位置和箱子位置

代码

package exam1.q3;

import java.util.LinkedList;
import java.util.Scanner;

public class Main {

	private static class Status{
		int x, y;
		int bx, by;
		int st;
		public Status(int x, int y, int bx, int by, int st) {
			super();
			this.x = x;
			this.y = y;
			this.bx = bx;
			this.by = by;
			this.st = st;
		}
		
	}
	
	private static final int[][] step = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; 
	
	private static int n, m;
	
	private static int pushBox(char[][] map) {
		
		int px = -1, py = -1;
		int bx = -1, by = -1;

		boolean[][][][] visited = new boolean[n][m][n][m];
		
		for(int i = 0; i < n; i++) {
			for(int j = 0; j < m; j++) {
				if(map[i][j] == 'S') {
					px = i;
					py = j;
				}
				else if(map[i][j] == '0') {
					bx = i;
					by = j;
				}
			}
		}
		int st = 0;
		Status s = new Status(px, py, bx, by, st);
		LinkedList<Status> queue = new LinkedList<>();
		queue.addLast(s);
		visited[px][py][bx][by] = true;
		
		while(!queue.isEmpty()) {
			s = queue.pollFirst();
			px = s.x;
			py = s.y;
			bx = s.bx;
			by = s.by;
			st = s.st;
			for(int i = 0; i< 4; i++) {
				int nextx = px + step[i][0];
				int nexty = py + step[i][1];
				if(nextx >= 0 && nextx < n && nexty >= 0 && nexty < m) {
					if(!(map[nextx][nexty] == '#') && !visited[nextx][nexty][bx][by]) {
						if(nextx == bx && nexty == by) {
							int nextbx = bx + step[i][0], nextby = by + step[i][1];
							if(nextbx >= 0 && nextbx < n && nextby >= 0 && nextby < m && !(map[nextbx][nextby] == '#')){
								Status news = new Status(nextx, nexty, nextbx, nextby, st + 1);
								queue.addLast(news);
								visited[nextx][nexty][nextbx][nextby] = true;
								if(map[nextbx][nextby] == 'E') {
									return st + 1;
								}
							}
						}
						else{
							Status news = new Status(nextx, nexty, bx, by, st + 1);
							queue.addLast(news);
							visited[nextx][nexty][bx][by] = true;
						}
					}
				}
			}
		}
		return -1;
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		
		n = sc.nextInt();
		m = sc.nextInt();
		char[][] map = new char[n][m];
		
		
		for(int i = 0; i < n; i++) {
			String tmp = sc.next();
			map[i] = tmp.toCharArray();
		}
		
		System.out.println(pushBox(map));
		sc.close();
		
	}
	
}
复制代码

第四题:房间分配

题目

有 n 个房间,如今 i 号房间里的人须要被从新分配,分配的规则是这样的:先让 i 号房间里的人全都出来,接下来按照 i+1, i+2, i+3, … 的顺序依此往这些房间里放一我的,n号房间的的下一个房间是1号房间,直到全部的人都被从新分配。

如今告诉你分配完后每一个房间的人数以及最后一我的被分配的房间号 x,你须要求出分配前每一个房间的人数。数据保证必定有解,如有多解输出任意一个解。
输入描述:
第一行两个整数 n, x (2<=n<=10^5, 1<=x<=n),表明房间房间数量以及最后一我的被分配的房间号; 第二行n个整数 a_i(0<=a_i<=10^9) ,表明每一个房间分配后的人数
输出描述:
输出n个整数,表明每一个房间分配前的人数。

输入例子1:

3 1
6 5 1

输出例子1:

4 4 4

思路

知道最后一我的被分配的房间号 x,求最后一次被分配人原来的房间的人数 num,设最后一次被分配人原来的房间号为 startx,

(x + n) % – startx = num % n

原来的房间必定是当前人数最少的房间之一,由于在最后一次分配前其人数被置为 0,在分配完成以后,其人数必定是 num / n。

可是人数最少的房间不必定惟一,由于可能在最后一次分配前还有其余人数为 0 的房间。还须要判断当前位置与 startx 之间的全部房间的人数是否都大于最小值。

代码

package exam1.q4;

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		
		int n, x;
		n = sc.nextInt();
		x = sc.nextInt();
		long[] a = new long[n];
		long min = Long.MAX_VALUE;
		int startx = -1;

		for(int i = 0; i < n; i++) {
			a[i] = sc.nextInt();
			if(a[i] < min) {
				min = a[i];
			}
		}
		x--;
		sc.close();
		for(int i = 0; i < n; i++) {
			if(a[i] == min) {
				int tmp = x - i;
				if(tmp < 0)
					tmp += n;
				
				boolean f = true;
				for(int j = 1; j <= tmp; j++) {
					if(a[(i + j) % n] < min + 1){
						f = false;
						break;
					}
				}
				if(f){
					startx = i;
					break;
				}
			}
		}
		
		
		
		long remain = 0;
		if(x < startx) 
			remain = x + n - startx;
		else
			remain = x - startx;
		
		long round = min;
		for(int i = 0; i < n; i++)
			a[i] -= round;
			
		for(int i = 1; i <= remain; i++) 
			a[(startx + i) % n] -= 1;

		a[startx] = round * n + remain;
		for(int i = 0; i < n; i++) 
			System.out.print(a[i] + " ");
		
		System.out.println();
		
	}

}
复制代码

第五题:跳房子

题目

存在 n + 1 个房间,每一个房间依次为房间 1 2 3…i,每一个房间都存在一个传送门,i房间的传送门能够把人传送到房间 pi(1<=pi<=i),如今路人甲从房间 1 开始出发(当前房间 1 即第一次访问),每次移动他有两种移动策略:
A. 若是访问过当前房间 i 偶数次,那么下一次移动到房间i+1;
B. 若是访问过当前房间 i 奇数次,那么移动到房间pi;
如今路人甲想知道移动到房间n+1一共须要多少次移动;
输入描述
第一行包括一个数字 n(30%数据1 <= n <= 100,100%数据 1 <= n <= 1000),表示房间的数量,接下来一行存在 n 个数字 pi(1 <= pi <= i), pi 表示从房间 i 能够传送到房间 pi。
输出描述
输出一行数字,表示最终移动的次数,最终结果须要对1000000007 (10e9 + 7) 取模。

输入例子1:

2
1 2

输出例子1:

4

例子说明1:

开始从房间1 只访问一次因此只能跳到p1即 房间1, 以后采用策略A跳到房间2,房间2这时访问了一次所以采用策略B跳到房间2,以后采用策略A跳到房间3,所以到达房间3须要 4 步操做。

思路:

注意 pi(1 <= pi <= i),即第奇数次到达某一房间只能往前面的房间跳,同时也意味着,要跳到某个从未到达过的房间 i,必须通过偶数次 i – 1,同时 i – 1 以前的全部房间也必须通过了偶数次。

该条件保证了能够利用子结构的最优解

令 dp[i]表明第二次到达房间i通过的步骤。首先考虑第一次到达第 i 个房间,则必定是通过了两次房间 i – 1,即步数为 dp[i – 1] + 1;

同时,因为此次是第一次到达房间 i,接下来会跳到pi,此时的步数更新为 dp[i – 1] + 2。要想再次到达房间 i,则必定要再两次通过房间 i – 1。而此时前 i – 1 个房间的状态为:

除了第 pi 个房间通过了奇数次,其余房间都是偶数次,与第一次到达第 pi 个房间时的状态一致。而第一次到达pi时通过的步数为 dp[pi – 1] + 1;

此时要再次到达第i个房间,必须再次两次通过 i – 1,今后时的状态到两次通过 i – 1 的状态所需的步数,等于从初始状态到第二次到达房间 i – 1 时的状态所须要的步数减去从初始状态到第一次到达房间 pi 的状态所须要的步数,即 dp[i – 1] – (dp[pi – 1] + 1),再走一步,第二次到达房间 i。

综上,第二次到达房间 i 的步数的状态转移方程为为:

dp[i] = dp[i – 1] + 1 + 1 + dp[i – 1] – (dp[pi – 1] + 1) +1 = dp[i – 1] * 2 – dp[pi – 1] + 2

代码

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		long mod = 1000000007;
		int n = sc.nextInt();
		int[] next = new int[n + 1];
		for(int i = 1; i <= n; i++) {
			next[i] = sc.nextInt();
			
		}
		long[] dp = new long[n + 1];
		if(n == 1)
			System.out.println(1);
		else {

			for(int i = 1; i <= n; i++) {
				dp[i] = ((dp[i - 1] * 2) % mod - dp[next[i] - 1] + 2) % mod;
			}
		}
		System.out.println((dp[n]) % 1000000007);
		sc.close();
	}

}
复制代码

网易校招编程题

第一题:整理房间

题目

又到了周末,小易的房间乱得一团糟。 他但愿将地上的杂物稍微整理下,使每团杂物看起来都紧凑一些,没有那么乱。 地上一共有 n 团杂物,每团杂物都包含 4 个物品。第 i 物品的坐标用(ai,bi)表示,小易每次均可以将它绕着(xi, yi)逆时针旋转 90°,这将消耗他的一次移动次数。若是一团杂物的 4 个点构成了一个面积不为 0 的正方形,咱们说它是紧凑的。 由于小易很懒,因此他但愿你帮助他计算一下每团杂物最少须要多少步移动能使它变得紧凑
输入描述:
第一行一个数 n(1 <= n <= 100),表示杂物的团数。 接下来 4n 行,每4行表示一团杂物,每行 4 个数 ai, bi,xi, yi, (-104 <= xi, yi, ai, bi <= 104),表示第 i 个物品旋转的它自己的坐标和中心点坐标。
输出描述:
n行,每行1个数,表示最少移动次数。

输入例子1 :

4

1 1 0 0
-1 1 0 0
-1 1 0 0
1 -1 0 0
1 1 0 0
-2 1 0 0
-1 1 0 0
1 -1 0 0
1 1 0 0
-1 1 0 0
-1 1 0 0
-1 1 0 0
2 2 0 1
-1 0 0 -2
3 0 0 -2
-1 1 -2 0

输出例子1:

1
-1
3
3

例子说明1:

对于第一团杂物,咱们能够旋转第二个或者第三个物品1次。

思路

因为一共四个点,每一个点只有四种状态,故一共16种状态,搜索便可。因为是最少移动次数,故采用广度优先搜索。

须要注意的是如何计算一个点围绕另外一个点旋转顺时针旋转90度。

咱们在草稿纸上画一下,能够简单地得出点A(x1, y1)绕点B(x2, y2)逆时针旋转90度的公式为:

X1 = x2 – y1 + y2;
Y2 = x1 – x2 + y2;

但同时咱们也能够总结一下A绕B旋转任意角度的公式,在碰到相似题目的时候能够防止从新推导。

点A(x1, y1)绕点B(x2, y2)顺时针旋转θ度:

x = (x1 – x2) cosθ – (y1 – y2) sinθ + x2
y = (y1 – y2) cosθ +(x1– x2) sinθ + y2

代码

package tideTheRoom;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {

	private static class Point implements Cloneable {
		long x;
		long y;
		long a;
		long b;
		int cnt;

		public Point(long x, long y, long a, long b, int cnt) {
			super();
			this.x = x;
			this.y = y;
			this.a = a;
			this.b = b;
			this.cnt = cnt;
		}

		public void rotate() {
			if (this.cnt == 3)
				return;
			long tx = a - y + b;
			long ty = x - a + b;
			this.x = tx;
			this.y = ty;
			this.cnt++;
		}

		@Override
		public Point clone() {
			Object o = null;
			try {
				o = super.clone();
			} catch (CloneNotSupportedException e) {
			}
			return (Point) o;
		}
	}

	private static boolean check(Point[] p) {
		long[] dist = new long[6];
		int cnt = 0;
		for (int i = 0; i < 3; i++) {
			for (int j = i + 1; j < 4; j++) {
				dist[cnt++] = (p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y);
			}
		}
		Arrays.sort(dist);
		if (dist[0] == dist[1] && dist[0] == dist[2] && dist[0] == dist[3] && dist[4] == dist[5]
				&& !(dist[0] == dist[4]))
			return true;
		return false;
	}

	private static int bfs(Point[] p) {
		boolean[][][][] visited = new boolean[4][4][4][4];
		LinkedList<Point[]> que = new LinkedList<>();
		que.addLast(p);
		visited[0][0][0][0] = true;
		if (check(p))
			return 0;
		while (!que.isEmpty()) {
			Point[] f = que.pollFirst();

			for (int i = 0; i < 4; i++) {
				Point[] tmp = new Point[4];
				for (int j = 0; j < 4; j++) {
					tmp[j] = f[j].clone();
				}
				tmp[i].rotate();
				if (visited[tmp[0].cnt][tmp[1].cnt][tmp[2].cnt][tmp[3].cnt])
					continue;
				if (check(tmp)) {
					return tmp[0].cnt + tmp[1].cnt + tmp[2].cnt + tmp[3].cnt;
				}
				que.addLast(tmp);
				visited[tmp[0].cnt][tmp[1].cnt][tmp[2].cnt][tmp[3].cnt] = true;
			}
		}
		return -1;
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		int n = sc.nextInt();
		while ((n--) != 0) {
			Point[] p = new Point[4];
			for (int i = 0; i < 4; i++) {
				long x = sc.nextLong();
				long y = sc.nextLong();
				long a = sc.nextLong();
				long b = sc.nextLong();
				p[i] = new Point(x, y, a, b, 0);
			}
			System.out.println(bfs(p));
		}

		sc.close();
	}
}
复制代码

第二题:小易的字典

题目:

小易在学校中学习了关于字符串的理论, 因而他基于此完成了一个字典的项目。 小易的这个字典很奇特, 字典内的每一个单词都包含n个’a’和m个’z’, 而且全部单词按照字典序排列。 小易如今但愿你能帮他找出第k个单词是什么。
输入描述:
输入包括一行三个整数n, m, k(1 <= n, m <= 100, 1 <= k <= 109), 以空格分割。 输出描述:
出第k个字典中的字符串,若是无解,输出-1。

输入示例1:

2 2 6

输出示例1:

zzaa

示例说明:

字典中的字符串依次为aazz azaz azza zaaz zaza zzaa

思路

总体上是二分的思想。

首先,全部的组合数目的总数为𝐶𝑛𝑚+𝑛,从最高位开始,第一个字符是a仍是z将整个字符串划分为两个部分,若是第一个是a,则整个字符串在前𝐶𝑛−1𝑚+𝑛−1部分,不然,在𝐶𝑛−1𝑚+𝑛−1+1到𝐶𝑛𝑚+𝑛部分。因此,要查找第k个字符串,首先判断第一个字符,若是𝑘<=𝐶𝑛−1𝑚+𝑛−1,则说明第一个字符为a,在前𝑘>𝐶𝑛−1𝑚+𝑛−1部分查找由𝑛个a和𝑚个z组成的第k个字符串,即在后𝑛–1个字符串中查找有𝑛–1个a和𝑚个z组成的字符串,不然,在𝐶𝑛−1𝑚+𝑛−1+1到𝐶𝑛𝑚+𝑛部分部分查找,即在后𝑛–1个字符组成的字符串中查找由𝑛个a和𝑚–1个z组成的第𝑘–𝐶𝑛−1𝑚+𝑛−1个字符串,这就找到了划分子问题的方法。按照这个方法逐步日后查找,直到n或者m为0,说明,剩余的字符都为z或者a。

这里求组合数须要必定的技巧,具体参考求组合数

代码

package dictionary;
 
import java.util.Scanner;
import static java.lang.Math.log;
 
import java.math.BigInteger;
 
import static java.lang.Math.exp;
 
public class Main {
 
	public static long comb(int m, int n, long target) {// 计算假设a肯定以后,a以后的部分排列组合数
		if (m == 0 || n == 0)
			return 1;
		long sum = m + n;
		long k = 1;
		n = Math.min(m, n);// C(m+n) n=C(m+n) m 取最小便可
		for (int i = 0; i < n; i++) {
			k *= sum - i;
			k /= (i + 1);
			if (k > target)// 防止大数。若是k>target 则只进行list.add("a")和m--//a的个数减1。
							// 没有target -= k;所以不影响
				break;
		}
		return k;
	}
 
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
 
		int n = sc.nextInt();
		int m = sc.nextInt();
		int k = sc.nextInt();
 
		int tn = n, tm = m;
		StringBuilder sb = new StringBuilder();
		while (tn > 0 && tm > 0) {
			long c = comb(tn - 1, tm, k);
			// System.out.println(c);
 
			if (k <= c) {
				sb.append('a');
				tn--;
			} else {
				sb.append('z');
				k -= c;
				tm--;
			}
		}
		if (k != 1)
			System.out.println(-1);
		else {
			while (tn > 0) {
				sb.append('a');
				tn--;
			}
			while (tm > 0) {
				sb.append('z');
				tm--;
			}
			System.out.println(sb.toString());
		}
		sc.close();
	}
 
}
复制代码

刷了一些LeetCode题

第一题:LeetCode 42 Trapping Rain Water 和 LeetCode 407 Trapping Rain Water II

先刷到的LeetCode 407,是三维的状况,后来发现LeetCode 42,是二维的状况

在一开始作407时,用了一种的不断切割底部,遍历每次切割后高度为0的面积的方法,发现虽然能解决该问题,可是时间复杂度很高,最坏为

,致使不能AC

因为在想和写上述方法的过程当中耗费了大量时间,有没有想到其余太好的方法,我就去找了下题解。

在找题解的过程当中,发现有一道简化版的题目,作该题会对407的解法有启发做用。

题目

LeetCode 42

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!
Example:
Input:[0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6

题意

给 n 个柱子做为隔板,问柱子之间最多能存多少水

思路

根据木桶效应,能装多少水是由左右两边最高的柱子中的最矮柱子来决定的。

我一开始的作法是用两个数组 maxHeightLeft 和 maxHeightRight 分别记录位置 i 左边的最高柱子和右边的最高柱子。而后遍历从 1 到 n – 2,

若是 min(maxHeightLeft[i], maxHeightRight[i]) > height[i] 的话,答案ans += min(maxHeightLeft[i], maxHeightRight[i]) – height[i]

时间复杂度为

代码

import java.lang.Math;

public class Solution {
    public int trap(int[] height) {
        int len = height.length;
    	int[] maxHeightLeft = new int[len];
    	int[] maxHeightRight = new int[len];


    	for(int i = 1; i < len; i ++){
    		if(height[i - 1] > maxHeightLeft[i - 1])
    			maxHeightLeft[i] = height[i - 1];
    		else
    			maxHeightLeft[i] = maxHeightLeft[i - 1];
    	}
    	for(int i = len - 2; i >= 0; i --) {
    		if(height[i + 1] > maxHeightRight[i + 1])
    			maxHeightRight[i] = height[i + 1];
    		else
    			maxHeightRight[i] = maxHeightRight[i + 1];
    	}
    	
    	int sum = 0;
    	for(int i = 1; i < len - 1; i ++){
    		int shortEdge = Math.min(maxHeightLeft[i], maxHeightRight[i]);
    		if(shortEdge > height[i])
    			sum += shortEdge - height[i];
    	}
    	return sum;
    }
}
复制代码

LeetCode 407 Trapping Rain Water II

Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining.

Note: Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000.

Example:
Given the following 3×6 height map: [
[1,4,3,1,3,2],
[3,2,1,3,2,4],
[2,3,3,2,3,1]
]
Return 4.

The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]]before the rain.
After the rain, water is trapped between the blocks. The total volume of water trapped is 4.

思路
利用优先队列(小顶堆)

首先将边缘的柱子所有入队,对中元素将整个矩阵围起来,做为最外层。而次外层的某个位置若是能存住水的话,其容量大小必定与其最紧靠的外一层位置的柱子高度相关

因而每次将围住矩阵的优先队列中的元素,从最小的元素往里扩展,并用扩展到的位置的高度来更新已遍历的外层位置的最高高度

代码

import java.util.Comparator;
import java.util.PriorityQueue;
import static java.lang.Math.max;

public class Solution1 {

	private final static int[][] steps = new int[][] { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };

	private class Point {
		int x;
		int y;
		int height;

		public Point(int x, int y, int height) {
			super();
			this.x = x;
			this.y = y;
			this.height = height;
		}

	}

	private class Comparator1 implements Comparator<Point> {

		@Override
		public int compare(Point o1, Point o2) {
			if (o1.height > o2.height)
				return 1;
			return -1;
		}

	}

	public int trapRainWater(int[][] heightMap) {
		int ans = 0;
		int lenx = heightMap.length;
		if (lenx < 3)
			return 0;
		int leny = heightMap[0].length;
		if (leny < 3)
			return 0;
		boolean[][] visited = new boolean[lenx][leny];

		PriorityQueue<Point> que = new PriorityQueue<>(new Comparator1());
		for (int i = 0; i < lenx; i++) {
			que.add(new Point(i, 0, heightMap[i][0]));
			visited[i][0] = true;
			que.add(new Point(i, leny - 1, heightMap[i][leny - 1]));
			visited[i][leny - 1] = true;
		}
		for (int i = 1; i < leny - 1; i++) {
			que.add(new Point(0, i, heightMap[0][i]));
			visited[0][i] = true;
			que.add(new Point(lenx - 1, i, heightMap[lenx - 1][i]));
			visited[lenx - 1][i] = true;
		}
		int maxHeight = -1;

		while (!que.isEmpty()) {
			Point cur = que.poll();

			maxHeight = max(maxHeight, cur.height);
			for (int i = 0; i < 4; i++) {
				int nextX = cur.x + steps[i][0];
				int nextY = cur.y + steps[i][1];
				if (nextX >= 0 && nextX < lenx && nextY >= 0 && nextY < leny && !visited[nextX][nextY]) {
					//System.out.println(nextX + " " + " " + nextY + " " + maxHeight + " " + heightMap[nextX][nextY]);
					if (heightMap[nextX][nextY] < maxHeight) {

						ans += maxHeight - heightMap[nextX][nextY];
					}
					visited[nextX][nextY] = true;
					que.add(new Point(nextX, nextY, heightMap[nextX][nextY]));
				}
			}
		}
		return ans;
	}
}
复制代码

第二题:leetcode 207 Course Schedule

题目

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair:[0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

Example 1:

Input: 2, [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

Note:

The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. You may assume that there are no duplicate edges in the input prerequisites.

思路

明显的拓扑排序,复习一下,顺便复习了一下图的链式前向星表示法

代码

package leetcode_207_course_schedule;

import java.util.HashSet;
import java.util.Stack;

public class Solution {

	private class Edge {
		int next;
		int to;

		public Edge(int next, int to) {
			super();
			this.next = next;
			this.to = to;
		}
	};

	int[] head;
	Edge[] edges;
	int cntE;

	public void add(int u, int v) {
		Edge e = new Edge(head[u], v);
		edges[++cntE] = e;
		head[u] = cntE; // 第一条边为当前边

	}

	public boolean canFinish(int numCourses, int[][] prerequisites) {
		Stack<Integer> stack = new Stack<>();
		int len = prerequisites.length;
		HashSet<Integer> set = new HashSet<>();

		int[] indegree = new int[numCourses];
		head = new int[numCourses];
		edges = new Edge[len + 1];

		for (int i = 0; i < len; i++) {
			indegree[prerequisites[i][1]]++;
			add(prerequisites[i][0], prerequisites[i][1]);
		}

		for (int i = 0; i < numCourses; i++) {
			set.add(i);
		}

		for (int i = 0; i < numCourses; i++) {
			if (indegree[i] == 0)
				stack.add(i);
		}

		while (!stack.empty()) {
			int cur = stack.pop();
			set.remove(cur);
			for (int i = head[cur]; i != 0; i = edges[i].next) {
				indegree[edges[i].to]--;
				if (indegree[edges[i].to] == 0)
					stack.push(edges[i].to);
			}
		}
		if (set.size() == 0)
			return true;
		else
			return false;
	}

}
复制代码

第三题:LeetCode 871 Minimum Number of Refueling Stops

以前面试一道百度的题,大体是:开车从起点到终点,中间有若干个加油站,汽车的油箱容量无限,求能到达终点的最少加油次数;若是没有办法到达终点,输出-1。我在网上找到了一道相似的题目,就是就是LeetCode 871。

题目

A car travels from a starting position to a destination which is target miles east of the starting position.

Along the way, there are gas stations. Each station[i] represents a gas station that is station[i][0] miles east of the starting position, and has station[i][1] liters of gas.

The car starts with an infinite tank of gas, which initially has startFuel liters of fuel in it. It uses 1 liter of gas per 1 mile that it drives.

When the car reaches a gas station, it may stop and refuel, transferring all the gas from the station into the car.

What is the least number of refueling stops the car must make in order to reach its destination? If it cannot reach the destination, return -1.

Note that if the car reaches a gas station with 0 fuel left, the car can still refuel there. If the car reaches the destination with 0 fuel left, it is still considered to have arrived.

Example 1:

Input: target = 1, startFuel = 1, stations = []
Output: 0
Explanation: We can reach the target without refueling.

Example 2:

Input: target = 100, startFuel = 1, stations = [[10,100]]
Output: -1
Explanation: We can’t reach the target (or even the first gas station).

Example 3:

Input: target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]]
Output: 2
Explanation:
We start with 10 liters of fuel.
We drive to position 10, expending 10 liters of fuel. We refuel from 0 liters to 60 liters of gas. Then, we drive from position 10 to position 60 (expending 50 liters of fuel),
and refuel from 10 liters to 50 liters of gas. We then drive to and reach the target. We made 2 refueling stops along the way, so we return 2.

Note:

1 <= target, startFuel, stations[i][1] <= 10^9
0 <= stations.length <= 500 0 < stations[0][0] < stations[1][0] < … < stations[stations.length-1][0] < target

思路

若是须要加油,若是可以预料到在哪个加油站到达以前缺油,必定是在该加油站以前通过的加油站中油量最多的加油站加油最划算(一次加油加得更多),全部用一个堆维护全部的通过的加油站,保证堆头元素必定是已经通过但没有在该站加过油的油量最多的加油站。当遇到在某个站以前会缺油,就从堆头取加油站,并使得加油次数加一;当全部已通过的加油站都取完且加完油,仍是不能到达下一个加油站,说明以前全部的加油站的油量加上初始油量都不能支持到达下一个加油站,返回-1。

代码

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

public class Solution {
	public class GasStation {
		public int pos;
		public int gas;

		public GasStation(int pos, int gas) {
			super();
			this.pos = pos;
			this.gas = gas;
		}

	}

	class GasStationComparator1 implements Comparator<GasStation> {
		@Override
		public int compare(GasStation o1, GasStation o2) {
			return o1.pos - o2.pos;
		}
	}

	class GasStationComparator2 implements Comparator<GasStation> {
		@Override
		public int compare(GasStation o1, GasStation o2) {
			return o2.gas - o1.gas;
		}
	}

	public int minRefuelStops(int target, int startFuel, int[][] stations) {
		int num_GS = stations.length;
		List<GasStation> list = new ArrayList<>();
		for (int[] gs : stations) {
			list.add(new GasStation(gs[0], gs[1]));
		}
		list.add(new GasStation(target, 0));
		list.sort(new GasStationComparator1());
		int gas = startFuel;
		PriorityQueue<GasStation> heap = new PriorityQueue<>(new GasStationComparator2());
		int cnt = 0;

		for (int i = 0; i < num_GS + 1; i++) {
			GasStation gs = list.get(i);
			
			
			if (gs.pos > gas) {
				while (gs.pos > gas && !heap.isEmpty()) {
					//System.out.println(heap.peek().pos + " " + heap.peek().gas);
					gas += heap.poll().gas;
					
					cnt++;
				}
				if (gs.pos > gas && heap.isEmpty()) {
					return -1;
				}
			}
			heap.add(gs);
		}
		return cnt;
	}
}
复制代码

结语

以上是我这段时间校招中积累的一些题,最近感冒严重,暂时先更新这些。各位小伙伴们加油,有不争取的地方也欢迎你们指正。

我在参加掘金技术征文,活动进行中,详情请戳👉(15) 秋招求职时,写文就有好礼相送 | 掘金技术征文

相关文章
相关标签/搜索