校招中我,細細品味了這些題... | 掘金技術徵文

本人北京某高校計算機碩士,總以爲本科畢業仍是不久前的事情,轉眼間研究生也要畢業了,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) 秋招求職時,寫文就有好禮相送 | 掘金技術徵文

相關文章
相關標籤/搜索