这是典型的三道动态规划,在笔试当中,有可能会被举一反三地考。
1、求一个数组最大连续子数组之和。
示例:int[] nums = {2,1,3,6,-10,5}; 最大连续子数组是2,1,3,6,所以输出12。
2、求一个数组的最长递增子序列。
示例:int[] nums = {2,1,3,6,-10,5}; 最长递增子序列是1,3,6,所以输出3。
3、求两个字符串的最大公共子串。
示例:String str1 = "bcfde"; String str2 = "abcef"; 输出bc。
4、求两个字符串的最大公共子序列的个数。
示例:String str1 = "bcfde"; String str2 = "abcef"; 最大公共子序列是bcf,输出3。
第一个问题:
最经典!连续,和最大。这两个重点,用两个变量标记,一个是localmax = Math.max(nums[i], nums[i]+localmax); 什么意思呢?当前值nums[i]与nums[i]+localmax取大,比如,上一步算出的localmax为-10,现在nums[i] = 5,很明显,就选择5作为localmax,前面所有的数将不再最终候选子数组中。另一个是max = Math.max(max, localmax),这很明显了,你算出来了localmax,自然要和上一步计算出的max作比较,取大值。
第二个问题:
问题稍复杂。dp[i]表示以下标i结尾的子数组中最长递增子序列的长度,状态转移 方程为:dp[i] = Math.max(dp[j]+1, dp[i]),这里的j为1<=j<i;
第三个问题:
序贯决策,高大上!用一个二维矩阵记录结果。举例子来说
需要记录的是,max = 2, maxindex = 1; 返回的是str1.subString(maxindex+1-max, maxindex+1);
第三个问题:
在上一个问题基础上有了拓展,不需要连续,只要有相同即可,同样用二维矩阵记录结果,
最后输出3即可。
代码:
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str1 = "bcfde";
String str2 = "abcef";
System.out.println("最大公共子串:"+getLCString(str1, str2));
System.out.println("最长公共子序列个数:"+LCS(str1, str2));
int[] nums = {2,1,3,6,-10,5};
System.out.println("一个数组的最长递增子序列:"+getLIS(nums));
System.out.println("一个数组的最大连续子数组之和:"+getMaxSubArr(nums));
}
/**
* 一个数组的最长递增子序列
* @param nums
* @return
*/
public static int getLIS(int[] nums){
if(nums == null || nums.length == 0)
return 0;
int max = 1;
int[] dp = new int[nums.length];
for(int i=0; i<nums.length; i++){
dp[i] = 1;
for(int j=0; j<i; j++){
if(nums[j] < nums[i]){
dp[i] = Math.max(dp[i], dp[j]+1);
max = Math.max(max, dp[i]);
}
}
}
return max;
}
/**
* 两个字符串的最大公共子串
* @param str1
* @param str2
* @return
*/
public static String getLCString(String str1, String str2) {
int len1 = str1.length();
int len2 = str2.length();
if(str1 == null || str2 == null || len1 == 0 || len2 == 0){
return null;
}
int maxlen = 0;
int endindex = 0;
int[][] dp = new int[len1][len2];
for(int i=0; i<len1; i++){
for(int j=0; j<len2; j++){
if(i == 0 || j == 0) {
dp[i][j] = str1.charAt(i) == str2.charAt(j) ? 1 : 0;
}else{
dp[i][j] = str1.charAt(i) == str2.charAt(j) ? dp[i-1][j-1]+1 : 0;
}
if(maxlen < dp[i][j]){
maxlen = dp[i][j];
endindex = i;
}
}
}
return str1.substring(endindex+1-maxlen, endindex+1);
}
/**
* 两个字符串的公共子序列
* @param str1
* @param str2
* @return
*/
public static int LCS(String str1, String str2){
int len1 = str1.length();
int len2 = str2.length();
int max = 0;
int[][] dp = new int[len2+1][len1+1];
for(int i=0; i<=len2; i++){
dp[i][0] = 0;
}
for(int j=0; j<=len1; j++){
dp[0][j] = 0;
}
for(int i=1; i<=len1; i++){
for(int j=1; j<=len2; j++){
if(str1.charAt(i-1) == str2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1] +1;
}else{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
if(max < dp[i][j]){
max = dp[i][j];
}
}
}
return max;
}
public static void print(int[][] dp, String X, String Y, int i, int j){
if(i == 0 || j == 0){
return;
}
if(X.charAt(i-1) == Y.charAt(j-1)){
System.out.print(X.charAt(i-1));
print(dp, X, Y, i-1, j-1);
}else if(dp[i-1][j] >= dp[i][j]){
print(dp, X, Y, i-1, j);
}else{
print(dp, X, Y, i, j-1);
}
}
/**
* 一个数组最大连续子数组之和
* @param nums
* @return
*/
public static int getMaxSubArr(int[] nums){
int localmax = nums[0];
int max = nums[0];
for(int i=1; i<nums.length; i++){
localmax = Math.max(nums[i], localmax+nums[i]);
max = Math.max(max, localmax);
}
return max;
}
}
结果: