劍指offer: 自上而下分析問題自下而上解決問題系列(java)

面試題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)的構成情況(分成哪些子部分),自下而上利用循環去實現編碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章