面試題48:最長不含重複字符的子字符串
題目:
請從字符串中找出一個最長的不包含重複字符的子字符串,計算該最長字符串的長度。假設字符串中只包含'a'~'z'的字符。例如在字符串arabcacfr,最長的不含重複字符的子字符串是acfr,長度爲4。
解答:
定義函數f(i)表示:以第i個字符結尾的不包含重複字符的子字符串的最長長度。若我們知道f(i)時,我們也知道f(i-1)了。定義d爲:第i個字符和它上次出現在字符串中的位置的距離。從左到右逐個遍歷字符串中的字符。
(1) 若第i個字符之前沒有出現過。f(i) = f(i-1)+1
(2)若第i個字符之前出現過。又分爲兩種情況:
(a) d <= f(i-1) (重複出現的字符在 以i-1爲結尾的最長子字符串中) f(i) = d
(b) d > f(i-1) (重複出現的字符在 以i-1爲結尾的最長子字符串之外)f(i) = f(i-1)+1
import java.util.Arrays;
public class LongestSubstringWithoutDuplication48 {
public static int longestSubstringWithoutDuplication48(String str) {
if(str == null || str.length() <=0) {
return 0;
}
int pre = 0; // 以前一個元素結尾的子字符串的長度
int cur = 0; // 以當前元素爲結尾的子字符串的長度
int max = 0; // 最大的子字符串的長度
int[] arr = new int[26]; // 下標對應26個字母
Arrays.fill(arr, -1); // 初始化爲-1
for(int i=0; i<str.length(); i++) {
int index = (str.charAt(i)-'a'); // 第i個字符對應的數組下標
// 當前元素之前沒有出現過 或者 出現過但兩者的距離大於 pre 時
if(arr[index] < 0 || (i - arr[index]) > pre) {
cur = pre+1;
}else { // 當前元素與之最近相同的元素的距離 小於或等於 pre 時
cur = i - arr[index];
}
arr[index] = i; // 記錄第i個字符出現的位置
if(cur>pre) {
max=cur;
}
pre = cur;
}
return max;
}
public static void main(String[] args) {
System.out.println("arabcacfr"+longestSubstringWithoutDuplication48("arabcacfr"));
System.out.println("arabcacfr"+longestSubstringWithoutDuplication48("arabcacfr"));
System.out.println("arabcac"+longestSubstringWithoutDuplication48("arabcac"));
System.out.println("arabc"+longestSubstringWithoutDuplication48("arabc"));
System.out.println("arab"+longestSubstringWithoutDuplication48("arab"));
}
}
面試題47:禮物的最大價值
題目:
在一個mXn的棋盤的每一格都放有一個禮物,每個禮物都有一定的價值(價值大於0)。你可以從棋盤的左上角開始拿格子裏的禮物,並每次向右或向下移動一個,直到到達棋盤的右下角。給定一個棋盤及其上面的禮物,請計算你最多能拿到多少價值的禮物。
解答:
定義函數f(i,j)表示爲:到達座標(i,j)位置時能拿到禮物總和的最大值。根據題目要求我們到達位置(i,j)有兩條路徑(i-1,j)和(i,j-1),所以f(i,j) = max( f(i-1,j),f(i,j-1) ) + gift[i,j] ,gift[i,j]爲位置(i,j)上禮物的價值。
public class GetMaxValueSolution47 {
public static int getMaxValueSolution47(int[][] mat){
if(mat == null || (mat.length<=0 && mat[0].length<=0)) {
return 0;
}
int[] dp = new int[mat[0].length]; // 存儲每列最大值
for(int i=0; i<mat.length; i++) { // 遍歷每一行
dp[0] += mat[i][0];
for(int j=1; j<mat[0].length; j++) { // 遍歷每一列
dp[j] = Math.max(dp[j], dp[j-1])+mat[i][j];
}
}
return dp[mat[0].length-1];
}
public static void main(String[] args) {
int[][] mat = {{1,10,3,8},
{12,2,9,6},
{5,7,4,11},
{3,7,16,5}};
System.out.println(getMaxValueSolution47(mat));
}
}
面試題46:把數字翻譯成字符串 (類似於青蛙跳臺階)
題目:
給定一個數字,我們按照如下規則把它翻譯成字符串:0翻譯成'a', 1翻譯成'b',..., 25翻譯成'z'.一個數字可有多個翻譯。例如,12258有5中不同的翻譯,分別是"bccfi"、"bwfi"、"bczi"、"mcfi"、"mzi"。請編程實現一個函數,來計算一個數字有多少種不同的翻譯方法。
解答: 自上而下分析問題,自下而上解決問題。將數字轉化成字符串,設函數f(i)表示 第i個字符爲結尾的翻譯方法的種數。
(1)當 第(i-1)個字符 *10+第i個字符 > 25 時,f(i) = f(i-1);
(2)當 第(i-1)個字符 *10+第i個字符 < 25 時, f(i) = f(i-1)+f(i-2);
public class GetTranslation46 {
public static int getTranslation46(int num) {
if(num < 0) {
return 0;
}
String numStr = String.valueOf(num); // 將數字轉化爲字符串
int len = numStr.length();
//if(len == 1) {return 1;} // 只有一個字符返回1
int[] arr = new int[len+1]; // 輔助數組
arr[0] = 1;
arr[1] = 1;
for(int i=1; i<len; i++) { // 從第2個字符開始
if(((numStr.charAt(i-1)-'0')*10+(numStr.charAt(i)-'0')) > 25) {
arr[i+1] = arr[i]; // f(i) = f(i-1)
}else {
arr[i+1] = arr[i]+arr[i-1]; // f(i) = f(i-1)+f(i-2)
}
}
return arr[len];
}
public static void main(String[] args) {
System.out.println(getTranslation46(12258)); // 5
System.out.println(getTranslation46(122)); // 3
System.out.println(getTranslation46(1225)); // 5
System.out.println(getTranslation46(12)); // 2
System.out.println(getTranslation46(1)); // 1
System.out.println(getTranslation46(0)); // 1
System.out.println(getTranslation46(-1)); // 0
}
}
面試題42:連續子數組的最大和
題目:
輸入一個整型數組,數組裏有正數也有負數。數組中的一個或者連續多個組成一個子數組。求所有子數組的和的最大值。要求時間複雜度爲O(n)。 例如輸入的數組爲{1,-2,3,10,-4,7,2,-5},和最大的子數組爲{3,10,-4,7,2} 和爲18 。
解答:
假設f(i)表示:以第i個數字結尾的子數組的最大和。從頭到尾遍歷每一個數字。
(1) 當i=0或者f(i-1)<=0時,f(i)=arr[i]
(2) 當i!=0並且f(i-1)>0時,f(i)=f(i-1)+arr[i]
public class FindGreatestSumOfSubArray42 {
public static int findGreatestSumOfSubArray42(int[] arr) {
if(arr == null || arr.length==0) {
return 0;
}
int max = Integer.MIN_VALUE; // 存儲最大值
int cur = 0; // 當前最大值
for(int a:arr) {
cur = cur>=0 ? (cur+a):a;
max = Math.max(cur, max);
}
return max;
}
public static void main(String[] args) {
int[] arr = {1,-2,3,10,-4,7,2,-5};
System.out.println(findGreatestSumOfSubArray42(arr));
}
}
總結: 利用遞歸的思想去分析問題,定義f(i),再去分析f(i)的構成情況(分成哪些子部分),自下而上利用循環去實現編碼。