剑指offer: 自上而下分析问题自下而上解决问题系列(java)

题目: 一个字符串s由ABC组成,长度为n,s经过修改变为s`,长度为k,s`是"ABCABCABC..."的子字符串。求s变为s`的最小改动次数。

输入
s:字符串
n:s的长度
k:s`的长度k

输出:s变为s`最小的改动次数

例子
输入:s="ABBB"   n=4    k=2
输出:2

解法

设f(n)为长为n的s满足"ABCABCABC..."的最长子串。

当:s[n-1]-s[n] == {-1,2}时,f(n) = f(n-1)+1;
当:s[n-1]-s[n] != {-1,2}时,f(n) = 1;

public static int fun1(String str, int n, int k) {
	int cur = 0;  // 记录当前
	int last = 1; // 记录上一个
	int max = 0;  // 记录最大
	for(int i=1; i<n; i++) {
		if((str.charAt(i-1)-str.charAt(i) == -1) ||
				(str.charAt(i-1)-str.charAt(i) == 2)) {
			cur = last+1;
		}else {
			cur = 1;
		}
		if(cur > max) {
			max =cur;
		}
		last = cur;
	}
	return max >= k ? 0:(k-max);
}

面试题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)的构成情况(分成哪些子部分),自下而上利用循环去实现编码。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值