牛客网剑指offer-动态规划(java实现)

一、JZ42 连续子数组的最大和(简单)

  1. 动态规划,设动态规划列表 dp,dp[i] 代表以元素 array[i] 为结尾的连续子数组最大和。状态转移方程: dp[i] = Math.max(dp[i-1]+array[i], array[i])

具体思路如下:

1.遍历数组,比较 dp[i-1] + array[i] 和 array[i]的大小;

2.为了保证子数组的和最大,每次比较 sum 都取两者的最大值;

3.用max变量记录计算过程中产生的最大的连续和dp[i];

public int FindGreatestSumOfSubArray(int[] array) {
        int sum = 0;
        int max = array[0];
        for(int i=0;i<array.length;i++){
            // 优化动态规划,确定sum的最大值
            sum = Math.max(sum + array[i], array[i]);
            // 每次比较,保存出现的最大值
            max = Math.max(max,sum);
        }
        return max;
}

二、JZ85 连续子数组的最大和(二)(中等)

1、动态规划

import java.util.*;
public class Solution {
    /**
     * @param array int整型一维数组
     * @return int整型一维数组
     */
    public int[] FindGreatestSumOfSubArray (int[] array) {
        // write code here
        if(array.length == 1) return array;
        int sum = 0;
        int max = array[0];
        //子数组区间
        int left = 0;
        int right = -1;
        //记录最长区间
        int start = 0;
        int end = 0;
        for (int item : array) {
            right ++;
            if (sum + item < item ) {
                left = right;
            }
            sum = Math.max(sum + item, item);
            //找到更大或更长的子数组就更新值
            if (sum > max || sum == max && (right - left) > (end - start)) {
                max = sum;
                start = left;
                end = right;
            }
        }
        int[] result = new int[end - start + 1];
        int i = 0;
        while (start <= end) {
            result[i] = array[start];
            i++;
            start ++;
        }
        return result;
    }
}

三、JZ69 跳台阶(简单)

1、动态规划

public class Solution {
    public int jumpFloor(int target) {
        if(target == 0 || target == 1) return 1;
        if(target == 2) return 2;
        int number1 = 1, number2 = 2;
        for(int i =3;i<= target;i++) {
            if(i%2 == 1) {
                number1 += number2;
            } else {
                number2 += number1;
            }
        }
        return (target)%2 ==1 ? number1 : number2;
    }
}

2、递归

public class Solution {
    public int jumpFloor(int target) {
        //这里第0项为1,第1项为1
        if(target <= 1)
            return 1;
        else
            //递归子问题相加
            return jumpFloor(target - 1) + jumpFloor(target - 2); 
    }
}

四、JZ10 斐波那契数列(简单,跟第三题代码非常相似)

  1. 动态规划

public class Solution {
    public int jumpFloor(int target) {
        if(target == 0 || target == 1) return 1;
        if(target == 2) return 2;
        int number1 = 1, number2 = 2;
        for(int i =3;i<= target;i++) {
            if(i%2 == 1) {
                number1 += number2;
            } else {
                number2 += number1;
            }
        }
        return (target)%2 ==1 ? number1 : number2;
    }
}

2、递归

public class Solution {
    public int jumpFloor(int target) {
        //这里第1项为1,第2项为1
        if(target <= 2)
            return 1;
        else
            //递归子问题相加
            return jumpFloor(target - 1) + jumpFloor(target - 2); 
    }
}

五、JZ19 正则表达式匹配(较难)

1、解题思路:

假设主串为 str,模式串为 pattern 从最后一步出发,需要关注最后进来的字符。假设 str 的长度为 n ,pattern的长度为 m ,关注正则表达式 pattern的最后一个字符是谁,它有三种可能,正常字符、'*' 和 '.'(点),那针对这三种情况讨论即可,如下:

1、如果 pattern的最后一个字符是正常字符,那就是看str[n−1] 是否等于 pattern[m−1],相等则看str [0..n−2]与 pattern [0..m−2],不等则是不能匹配,这就是子问题。

2、如果 pattern 的最后一个字符是 ‘.’,它能匹配任意字符,直接看 str [0..n−2]与 pattern [0..m−2]

3、如果 pattern 的最后一个字符是‘*’,它代表 pattern[m-2]=c 可以重复0次或多次,它们是一个整体 c*

情况一:str[n-1] 是 0 个 c,pattern最后两个字符废了,能否匹配取决于 str [0..n−1]和 pattern [0..m−3]是否匹配

情况二:str[n-1] 是多个 c 中的最后一个(这种情况必须 str[n-1]=c 或者 c='.'),所以str 匹配完往前挪一个,pattern 继续匹配,因为可以匹配多个,继续看str[0..n−2]和 pattern[0..m−1]是否匹配。

转移方程

f[i][j] 代表 str 的前 i 个和 pattern 的前 j 个能否匹配

对于前面两个情况,可以合并成一种情况 f[i][j]=f[i−1][j−1]

对于第三种情况,对于 c* 分为看和不看两种情况

不看:直接砍掉正则串pattern 的后面两个, f[i][j]=f[i][j−2]

看:正则串pattern 不动,主串str前移一个,f[i][j]=f[i−1][j]

初始条件

特判:需要考虑空串空正则

空串和空正则是匹配的,f[0][0]=true

空串和非空正则,不能直接定义 true 和 false,必须要计算出来。(比如str= '' '' ,pattern=a*b*c*)

非空串和空正则必不匹配,f[1][0]=...=f[n][0]=false

非空串和非空正则,那肯定是需要计算的了。

大体上可以分为空正则和非空正则两种,空正则也是比较好处理的,对非空正则我们肯定需要计算,非空正则的三种情况,前面两种可以合并到一起讨论,第三种情况是单独一种,那么也就是分为当前位置是 '*' 和不是 '*' 两种情况了。

结果

我们开数组要开 n+1 ,这样对于空串的处理十分方便。结果就是 f[n][m]

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param str string字符串
     * @param pattern string字符串
     * @return bool布尔型
     */
    public boolean match (String str, String pattern) {
        // write code here
        int n = str.length();
        int m = pattern.length();
        boolean[][] f =new boolean[n + 1][m + 1];
        for(int i= 0;i<=n;i++) {
            for(int j=0;j<=m;j++) {
                //分成空正则和非空正则两种
                if(j == 0) {
                    f[i][j] = i == 0;
                } else {
                    //非空正则分为两种情况 * 和 非*
                    if(pattern.charAt(j - 1) != '*') {
                        if(i > 0 && (str.charAt(i -1) == pattern.charAt(j-1) || pattern.charAt(j-1) == '.')) {
                           f[i][j] = f[i -1][j-1];
                        }
                    } else {
                            //碰到 * 了,分为看和不看两种情况
                            //不看
                            if(j >= 2) {
                                f[i][j] |= f[i][j-2];
                            }
                            //看
                            if(i >= 1 && j >= 2 && (str.charAt(i -1) == pattern.charAt(j -2) || pattern.charAt(j -2) == '.')) {
                                f[i][j] |= f[i-1][j];
                            }
                        }
                }
            }
        }
          return f[n][m];
    }
}

六、JZ71 跳台阶扩展问题(简单)

1、动态规划

public class Solution {
    public int jumpFloorII(int target) {
        if(target == 0 || target == 1) return 1;
        int result = 1;
        for(int i = 2;i<=target;i++) {
            result *= 2;
        }
        return result;
    }
}

2、递归

public class Solution {
    public int jumpFloorII(int target) {
        //1或0都是1种
        if(target <= 1) 
            return 1;
        //f(n) = 2*f(n-1)
        return 2 * jumpFloorII(target - 1); 
    }
}

七、JZ70 矩形覆盖(中等)

1、动态规划

public class Solution {
    public int rectCover(int target) {
        if(target == 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;
        int number1= 1, number2 = 2;
        for(int i = 3;i<= target;i++){
            if(i%2 == 1) {
                number1 += number2;
            } else {
                number2 += number1;
            }
        }
        return target%2 == 1 ? number1 : number2;
    }
}

2、递归

class Solution {
public:
    int rectCover(int number) {
        if(number < 1){
            return 0;
        }else if(number <= 2){
            return number;
        }else{
            return rectCover(number-1) + rectCover(number-2);//
        }
    }
};

八、JZ63 买卖股票的最好时机(一)(简单)

1、一次遍历,不断更新最低价格与当天卖出的最大利润

import java.util.*;
public class Solution {
    /**
     * 
     * @param prices int整型一维数组 
     * @return int整型
     */
    public int maxProfit (int[] prices) {
        // write code here
        if(prices.length == 0 || prices.length == 1) return 0;
        int min = prices[0];
        int count = 0;
        for(int i=1;i< prices.length;i++) {
            min = Math.min(prices[i], min);
            count = Math.max(prices[i] - min, count);
        }
        return count;
    }
}

九、JZ47 礼物的最大价值(中等)

1、动态规划

import java.util.*;
public class Solution {
    public int maxValue (int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        //第一列只能来自上方
        for(int i = 1; i < m; i++)
            grid[i][0] += grid[i - 1][0];
        //第一行只能来自左边
        for(int i = 1; i < n; i++)
            grid[0][i] += grid[0][i - 1];
        //遍历后续每一个位置
        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++)
                //增加来自左边的与上边的之间的较大值
                grid[i][j] += Math.max(grid[i - 1][j], grid[i][j - 1]);
        return grid[m - 1][n - 1];
    }
}

十、JZ48 最长不含重复字符的子字符串(中等)

1、使用辅助ArrayList

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return int整型
     */
    public int lengthOfLongestSubstring (String s) {
        // write code here
        char[] chars= s.toCharArray();
        int count = 0;
        int max = 0;
        List<Character> list = new ArrayList<>();
        for (int i = 0; i< chars.length ;i++) {
            if(i >= 1) {
                list.add(chars[i-1]);
                max = Math.max(max, list.size());
                if(list.contains(chars[i])) {
                    int index = list.indexOf(chars[i]);
                    list = list.subList(index + 1, list.size());
                } else {
                    max = Math.max(max, list.size() + 1);
                }
            } else {
                max = 1;
            }
        }
        return max;
    }
}

十一、JZ46 把数字翻译成字符串(中等)

1、动态规划

import java.util.*;
public class Solution {
    public int solve (String nums) {
        //排除0
        if(nums.equals("0"))  
            return 0;
        //排除只有一种可能的10 和 20
        if(nums == "10" || nums == "20")  
            return 1;
        //当0的前面不是1或2时,无法译码,0种
        for(int i = 1; i < nums.length(); i++){  
            if(nums.charAt(i) == '0')
                if(nums.charAt(i - 1) != '1' && nums.charAt(i - 1) != '2')
                    return 0;
        }
        int[] dp = new int[nums.length() + 1];
        //辅助数组初始化为1
        Arrays.fill(dp, 1);  
        for(int i = 2; i <= nums.length(); i++){
            //在11-19,21-26之间的情况
            if((nums.charAt(i - 2) == '1' && nums.charAt(i - 1) != '0') || (nums.charAt(i - 2) == '2' && nums.charAt(i - 1) > '0' && nums.charAt(i - 1) < '7'))
               dp[i] = dp[i - 1] + dp[i - 2];
            else
                dp[i] = dp[i - 1];
        }
        return dp[nums.length()];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值