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