本篇博客主要是解答此次校招中京東的筆試編程題,此次京東的筆試編程題比較難,涉及KMP算法、manacher算法等。文中的解法也是在觀看了左神(左程雲)9月20號在牛客網的直播後,本身花時間寫出來的。本篇博客不涉及算法的具體分析,主要是解題代碼及簡單的思路,關於其中的一些算法我會在後面的博客中詳細介紹。git
題目描述:
東東從京京那裏瞭解到有一個無限長的數字序列: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, ...(數字k在該序列中正好出現k次)。東東想知道這個數字序列的第n項是多少,你能幫幫他麼。github
輸入描述:
輸入包括一個整數n(1 ≤ n ≤ $10^{18}$) 。算法
輸出描述:
輸出一個整數,即數字序列的第n項。編程
示例:
輸入
169
輸出
18數組
解題思路:
這個主要是等差數列的求和,假設給的輸入爲k,則
$n(n-1)/2 < k < n(n+1)/2$,化簡獲得$\frac{1+\sqrt{1+8k}}{2} > 0$,即求上式知足時的最小正整數。同時此題要注意數的範圍。post
具體代碼:spa
public long findNum(long input){
return (long)Math.ceil((Math.sqrt(1 + 8 * input) -1)/2);
}複製代碼
題目描述:
東東在一本古籍上看到有一種神奇數,若是可以將一個數的數字分紅兩組,其中一組數字的和等於另外一組數字的和,咱們就將這個數稱爲神奇數。例如242就是一個神奇數,咱們可以將這個數的數字分紅兩組,分別是{2,2}以及{4},並且這兩組數的和都是4。東東如今須要統計給定區間中有多少個神奇數,即給定區間[l, r],統計這個區間中有多少個神奇數,請你來幫助他。code
輸入描述:
輸入包括一行,,一行中兩個整數l和r(1 ≤ l,r ≤ $10^9$,0 ≤ r - l ≤ $10^6$),以空格分割。rem
輸出描述:
輸出一個整數,即區間內的神奇數個數。字符串
示例:
輸入
1 50
輸出
4
解題思路:
這個題主要是判斷一個數是否是神奇數,且時間複雜度儘可能低。一個數判斷神奇數的問題實際上是一個揹包問題,好比242這個數,就是數組arr[2,2,4]中每一個數能夠用也能夠不用,是否能組成sum(arr) = 8的一半,能則是神奇數,反之則不能。還有若是和爲奇數則也不能。
具體代碼:
public int findMagicNumber(int start, int end) {
int ans = 0;
int[] digitals = new int[10];
boolean[] dp = new boolean[9 * 9];
Arrays.fill(digitals, -1);
Arrays.fill(dp, false);
dp[0] = true;
for (int i = start; i <= end; i++) {
int num = i;
int sum = 0;
int index = 0;
while (num > 0) {
int temp = num % 10;
digitals[index++] = temp;
sum += temp;
num = num / 10;
}
// 當各位數字和爲偶數時,才存在神奇數
if ((sum & 1) == 0){
for (int j = 0; j < digitals.length && digitals[j] != -1; j++) {
for (int k = sum; k >= 0; k--) {
// 用一維數組進行更新
dp[k] = dp[k] || j == 0 ? sum == digitals[j] : (k - digitals[j] < 0 ? false : dp[k-digitals[j]]);
}
if(dp[sum / 2]) {
ans++;
break;
}
}
}
Arrays.fill(digitals, -1);
Arrays.fill(dp, false);
dp[0] = true;
}
return ans;
}複製代碼
題目描述:
給定一個字符串s,請計算輸出含有連續兩個s做爲子串的最短字符串。注意兩個s可能有重疊部分。例如,"ababa"含有兩個"aba"。
輸入描述:
輸入包括一個字符串s,字符串長度length(1 ≤ length ≤ 50),s中每一個字符都是小寫字母。
輸出描述:
輸出一個字符串,即含有連續兩個s做爲子串的最短字符串。
示例:
輸入
abracadabra
輸出
abracadabracada
解題思路:
這個題目是關於KMP算法中求next數組的問題,咱們只需求出字符串最後一個字符後一位的next值。這個能算出來這個題目就解決了。
具體代碼:
public String shortestRepeatString(String str){
int[] next = new int[str.length() + 1];
Arrays.fill(ne xt, 0);
next[0] = -1;
next[1] = 0;
for (int i = 2; i < next.length; i++) {
char pre = str.charAt(i - 1);
int k = next[i - 1];
while (k != -1){
if (str.charAt(k) == pre) {
next[i] = next[i - 1] + 1;
break;
}
k = next[k];
}
}
return str + str.substring(next[str.length()]);
}複製代碼
題目描述:
合法的括號匹配序列被定義爲:
東東如今想知道使用上述的移除操做有多少種方案能夠把序列s變爲空。
若是兩個方案中有一次移除操做移除的是不一樣的右括號就認爲是不一樣的方案。
例如: s = "()()()()()",輸出1,由於每次都只能選擇被移除的左括號所相鄰的右括號。
s = "(((())))",輸出24,第一次有4種狀況,第二次有3種狀況,... ,依次類推,4 3 2 1 = 24。
*輸入描述:
輸入包括一行,一個合法的括號序列s,序列長度length(2 ≤ length ≤ 20)。
輸出描述:
輸出一個整數,表示方案數。
示例:
輸入
(((())))
輸出
24
解題思路:
從右往左遍歷,遇到左括號看左括號右邊右括號數量與左括號數量的差,並將這些差乘起來,即爲方案數。
具體代碼:
public int removeSolutions(String str){
int ans = 1;
int count = 0;
for (int i = str.length() - 1; i >= 0; i--) {
if (str.charAt(i) == ')') {
count++;
} else {
ans *= count;
count--;
}
}
return ans;
}複製代碼
題目描述:
京京和東東是好朋友。東東很喜歡迴文。迴文是指從前日後讀和從後往前讀是同樣的詞語。京京準備給東東一個驚喜,先取定一個字符串s,而後在後面附上0個或者更多個字母造成迴文,京京但願這個迴文越短越好。請幫助京京計算他可以獲得的最短的迴文長度。
輸入描述:
輸入包括一個字符串s,字符串s長度length(1 ≤ length ≤ 50)。
輸出描述:
輸出一個整數,表示京京可以獲得的最短的迴文長度。
示例:
輸入
abab
輸出
5
解題思路:
這個題目主要是manacher(馬拉車)算法的變形,主要是要求以最後一個字符結尾的時候的最大回文子串。
具體代碼:
public int longestPalindrome(String str){
int ans = 0;
// 字符串預處理
String strend = "$#";
for (int i = 0; i < str.length(); i++) {
strend += str.charAt(i);
strend += "#";
}
strend += "@";
// manacher算法
int[] radius = new int[strend.length()];
Arrays.fill(radius, 0);
int right = 0; //迴文最右邊界
int center = 0; //迴文中心
int left = 0; //迴文左邊界
for (int i = 1; i < strend.length(); i++) {
radius[i] = right > i ? Math.min(radius[2 * center - i], right - i): 1;
while (strend.charAt(i + radius[i]) == strend.charAt(i - radius[i])) {
radius[i] += 1;
}
if (right < i + radius[i]) {
right = i + radius[i];
center = i;
}
if(right == strend.length() - 1){
break;
}
}
return 2*str.length() - (radius[center] * 2 - 1)/2;
}複製代碼
歡迎你們交流和批評指正!
ps:本文有些公式顯示不正確,能夠訪問京東2018校招編程題解答(Java)