Letcode动态规划专题-中级

5.最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。
在这里插入图片描述
在这里插入图片描述
1.暴力破解的方法

class Solution {

    // 循环遍历
    public String longestPalindrome(String s) {
        int max = 0;
        String finalStr = s;

        for(int i=0;i<s.length();i++){
            for(int j=i+1;j<=s.length();j++){
                String judge = s.substring(i,j);
                if(check(judge)&&(judge.length()>max)){
                    max = judge.length();
                    finalStr = judge;
                }
            }
        }
        return finalStr;
    }

    // 校验是否为回文字符串 
    public boolean check(String str){
        // 检查字符串是否为回文字符串
        for(int i=0;i<=str.length()/2;i++){
            if(str.charAt(i)!=str.charAt(str.length()-i-1)){
                return false;
            }
        }
        return true;
    }
}

2.动态规划
动态规划方程:
在这里插入图片描述

i:表示左边
j:表示右边
dp[i][j] = (s[i]== s[j]) and (j-i<3 or dp[i+1][j-1])

由于dp[i][j]参考它左下方
(1)先升序填列
(2)后升序填行

在这里插入图片描述

public class Solution {

    public String longestPalindrome(String s) {
        // 特殊用例判断
        int len = s.length();
        if (len < 2) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;

        // dp[i][j] 表示 s[i, j] 是否是回文串
        boolean[][] dp = new boolean[len][len];
        char[] charArray = s.toCharArray();

        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        for (int j = 1; j < len; j++) {
            for (int i = 0; i < j; i++) {
                if (charArray[i] != charArray[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substring(begin, begin + maxLen);
    }
}

22.括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
在这里插入图片描述

1.原始而又质朴的暴力破解方法

  • 通过循环遍历获取所有的括号类型
    -只保留符合要求的括号类型
class Solution {
    public List<String> generateParenthesis(int n) {
        // 获取List数据
        List<String> result = new ArrayList<String>();
        // 递归生成所有的数据
        generateParenthesis(new char[2*n],0,result);
        return result;

    }




   public void generateParenthesis(char[] chars,int n, List<String> result){
       if(n==chars.length){
           if(check(chars)){
               result.add(new String(chars));
           }
       }else{
           // 首先先加左边
           chars[n] = '(';
          generateParenthesis(chars,n+1,result);

           // 接着后加右边
           chars[n] = ')';
           generateParenthesis(chars,n+1,result);

       }
   }


    

    // 校验的基础函数 
    public boolean check(char[] str){
        int balance = 0;
        for(int i=0;i<str.length;i++){
            if(str[i]=='('){
                balance++;
            }else if(str[i]==')'){
                balance--;
            }
            // 遍历过程中如果存在右括号大于左括号的数据
            if(balance<0){
                return false;
            }
        }
        // 最终判断是否 有效
        return balance==0;
    }
}

2.回溯(DFS+剪枝)

class Solution {

    List<String> list = new ArrayList<String>();

    public List<String> generateParenthesis(int n) {
        dfs("",n,n);
        return list;
    }


    public void dfs(String str,int left,int right){
        // 首先判断结束条件
        if(left==0&&right==0){
            list.add(str);
            return;
        }

        // 如果左边存在左括号
        if(left>0){
            dfs(str+"(",left-1,right);
        }

        // 如果右边的数量  大于 左边的数量 
        if(right>left){
            dfs(str+")",left,right-1);
        }
    }

}

‘’’
3.动态规划:
dp[i]表示i组括号的所有有效组合
dp[i] = “(dp[p]的所有有效组合)+【dp[q]的组合】”,其中 1 + p + q = i , p从0遍历到i-1, q则相应从i-1到0

/**
 * 动态规划
 * 从f(1),f(2),...,f(n-1)构建出f(n)
 *
 * 分析一个解的构成:必然从左括号开始,而这个开始的左括号必然对应着一个一个右括号,
 *     因此,任何一个解可以表示为 (A)B。
 * 令f(n) = (A)B  其中A和B是规模更小时问题的合法解,并且A、B可以为空,
 * 并且A中括号对数+B中括号对数之和为n-1
 *
 * 也就是说,可以从f(0),f(1),f(2),...,f(n-1构建出f(n),其中f0 = ""表示空解。
 */
public List<String> generateParenthesisDp(int n) {
    List<String>[] dp = new List[n+1];
    dp[0] = Arrays.asList("");

    for (int p = 1; p <= n; p++) {
        dp[p] = new ArrayList<>();
        for (int q = 0; q < p; q++) {
            for (String a : dp[q]) {
                for (String b : dp[p-q-1]) {
                    dp[p].add("("+a+")"+b);
                }
            }
        }
    }
    return dp[n];
}

53. 最大子数组和

在这里插入图片描述
1.暴力破解的方法

class Solution {
    public int maxSubArray(int[] nums) {
        // 最大子数组的和  
        // 先用暴力破解方法

        int max = Integer.MIN_VALUE;
        for(int i=0;i<=nums.length-1;i++){
            for(int j=i;j<nums.length;j++){
                int sum =0;
                for(int k=i;k<=j;k++){
                    sum +=nums[k];
                }
                if(sum>max){
                    max= sum;
                }
            }
        }

        return max;

    }
}

2.动态规划的方程
在这里插入图片描述

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        for(int i=1;i<nums.length;i++){
            if(dp[i-1]>0){
                dp[i] = dp[i-1] +nums[i]; 
            }else{
                dp[i] = nums[i];
            }
        }

        // 求动态规划中最大的数据
        int max = dp[0];
        for(int j=1;j<nums.length;j++){
            if(dp[j]>max){
                max = dp[j];
            }
        }
        return max;
    }
}


// 以第i位结尾的数字 
//        f(n-1)+nums[i]                    f(n-1)>0
//f(n) 
//        nums[i]                           f(n-1)<0

55. 跳跃游戏

在这里插入图片描述
1.动态规划
在这里插入图片描述

class Solution {
    public boolean canJump(int[] nums) {
        int n =nums.length;
        boolean[] f = new boolean[n];
        f[0] = true;
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(f[j]==true&&j+nums[j]>=i){
                    f[i]=true;
                    break;
                }
            }
            if(f[i]==false){ 
                return false;
            }
        }

        return true;
    }
}在这里插入代码片

2.贪心算法
在这里插入图片描述

class Solution {
    public boolean canJump(int[] nums) {
        int n = nums.length;
        int rightmost = 0;
        for(int i = 0;i<n;i++){
            if(i<=rightmost){
                rightmost = Math.max(rightmost,i+nums[i]);
                if(rightmost>=n-1){
                    return true;
                }
            }
        }
        return false;
    }
}

62. 不同路径

在这里插入图片描述

class Solution {
    public int uniquePaths(int m, int n) {
        // 首先定义动态规划方程
        int[][] dp = new int[m][n];

        for(int i=0;i<m;i++){
            dp[i][0] = 1;
        }
        
        for(int j=0;j<n;j++){
            dp[0][j] = 1;
        }

        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j] = dp[i][j-1] + dp[i-1][j];
            }
        }

        return dp[m-1][n-1];

    }
}

// 这道动态规划 是一道 比较简单的题目 

//f[0][i]=1
//f[i][0]=1
//f(m)(n) = f(m-1)(n)+f(m)(n-1)

不同路径 II

在这里插入图片描述

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
            // 定义动态规划方程
                int row = obstacleGrid.length;
                int col = obstacleGrid[0].length;
                int[][] dp = new int[row][col];
                boolean firstFlag = true;
                if(obstacleGrid[0][0]==1){
                    dp[0][0] = 0;
                    firstFlag = false;
                }else{
                    dp[0][0] = 1;
                }

                boolean flag = firstFlag;
                for(int i=1;i<row;i++){

                    if(flag){
                        if(obstacleGrid[i][0]==1){
                            dp[i][0] = 0;
                            flag = false;
                        }else{
                            dp[i][0] = 1;
                        }
                    }else{
                        dp[i][0] = 0;
                    }

                }

                flag = firstFlag;
                for(int j=1;j<col;j++){
                    if(flag){
                        if(obstacleGrid[0][j]==1){
                            dp[0][j] = 0;
                            flag = false;
                        }else{
                            dp[0][j] = 1;
                        }
                    }else{
                        dp[0][j] = 0;
                    }

                }


                for(int i=1;i<row;i++){
                    for(int j=1;j<col;j++){
                        if(obstacleGrid[i][j]==1){
                            dp[i][j] = 0;
                        }else{
                            dp[i][j] = dp[i-1][j]+dp[i][j-1];
                        }
                    }
                }

                return dp[row-1][col-1];

    }
}

// 大的动态规划方程
// f(m)(n) = f(m)(n-1) + f(m-1)(n)

// 主要是对边界条件的判断,只要出现其中一个1,则后边都为0

64. 最小路径和

在这里插入图片描述
在这里插入图片描述


class Solution {
    public int minPathSum(int[][] grid) {
        // 动态规划 
        int row = grid.length;
        int col = grid[0].length;
        int[][] dp = new int[row][col];

        dp[0][0] = grid[0][0];

        // 首先初始化x轴坐标
        for(int i=1;i<row;i++){
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }

        // 接着初始化y轴坐标
        for(int j=1;j<col;j++){
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }

        // 接着开始正式循环
        for(int i=1;i<row;i++){
            for(int j=1;j<col;j++){
                dp[i][j] = Math.min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);
            }
        }


        return dp[row-1][col-1];

    }
}


//  最小的路径和

// f(m)(n) = Min(f(m-1)(n)+nums[m][n], f(m)(n-1)+nums[m][n]);

96. 不同的二叉搜索树

在这里插入图片描述
在这里插入图片描述
结题思路:假设n个节点存在二叉排序树的个数是G(n),1为根节点,2为根节点,…,n为根节点,当1为根节点时,其左子树节点个数为0,右子树节点个数为n-1,同理当2为根节点时,其左子树节点个数为1,右子树节点为n-2,所以可得G(n) = G(0)G(n-1)+G(1)(n-2)+…+G(n-1)*G(0)
在这里插入图片描述

class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        
        for(int i = 2; i < n + 1; i++)
            for(int j = 1; j < i + 1; j++) 
                dp[i] += dp[j-1] * dp[i-j];
        
        return dp[n];
    }
}

139. 单词拆分

在这里插入图片描述
1.动态规划的方式

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
   Set<String> set = new HashSet<>(wordDict);
        boolean[] dp = new boolean[s.length()+1];

        // dp[i] 代表 前i位字符  [0]-[i-1]位置

        dp[0] = true;

        for(int i = 1;i<=s.length();i++){
            for(int j=0;j<i;j++){
                if(dp[j]&& set.contains(s.substring(j,i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
        //  字符串 动态规划
    //  int[] dp = new int[s.length-1];
    //  dp[i]表示前i位[0-s.length-1] 可由单词组成
    //  dp[i]= dp[j]&&check(s.substring(i,j))
}

152. 乘积最大子数组

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int maxProduct(int[] nums) {
          // 首先要用两个数组,最大,最小的
        int[] maxDp = new int[nums.length+1];
        int[] minDp = new int[nums.length+1];

        // 循环记录  以i为尾部,最小的
        maxDp[0] = nums[0];
        minDp[0] = nums[0];
        for(int i=1;i<nums.length;i++){
            maxDp[i] = Math.max(maxDp[i-1]*nums[i],Math.max(nums[i],minDp[i-1]*nums[i]));
            minDp[i] = Math.min(minDp[i-1]*nums[i],Math.min(nums[i],maxDp[i-1]*nums[i]));
        }

        int ans = nums[0];
        for(int i=1;i<nums.length;i++){
            ans = Math.max(ans,maxDp[i]);
        }
        return ans;
    }
}

198 打家劫舍

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int rob(int[] nums) {
        // 首先根据
            int n = 0;
            // 首先判断边界条件

            // 长度为1
            if(nums.length==1){
                return nums[0];
            }

            // 长度为2
            if(nums.length==2){
                return Math.max(nums[0],nums[1]);
            }

            // 长度为3
            int[] dp = new int[nums.length];
            dp[0] = nums[0];
            dp[1] = Math.max(nums[0],nums[1]);
            for(int i=2;i<nums.length;i++){
                dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
            }


            return dp[nums.length-1];
    }
}

221. 最大正方形

在这里插入图片描述
1.暴力破解
暴力法是最简单直观的做法,具体做法如下:

  • 遍历矩阵中的每个元素,每次遇到 11,则将该元素作为正方形的左上角;
  • 确定正方形的左上角后,根据左上角所在的行和列计算可能的最大正方形的边长(正方形的范围不能超出矩阵的行数和列数),在该边长范围内寻找只包含 11 的最大正方形;
  • 每次在下方新增一行以及在右方新增一列,判断新增的行和列是否满足所有元素都是 1。

在这里插入图片描述

class Solution {
  public int maximalSquare(char[][] matrix) {
        int maxSide = 0;
        // 首先获取最大的边长
        if(matrix==null||matrix.length==0||matrix[0].length==0){
            return 0;
        }
        int row = matrix.length;
        int cols = matrix[0].length;
        // 循环获取数据
        for(int i=0;i<row;i++){
            for(int j=0;j<cols;j++){
                // 循环获取数据,针对位数为1的就处理

                if(matrix[i][j]=='1'){
                    maxSide = Math.max(1,maxSide);
                    // 首先寻找最大的数据
                    boolean flag = true;
                    int maxBoundary = Math.min(row - i, cols - j);
                    for(int k=1;k<maxBoundary;k++){
                        // 首先校验中线的数据
                        if(matrix[i+k][j+k]=='0'){
                            break;
                        }

                        // 循环获取
                        for(int m=0;m<k;m++){
                            if(matrix[i+m][j+k]=='0'|| matrix[i+k][j+m]=='0'){
                                flag = false;
                                break;
                            }
                        }

                        if(flag){
                            maxSide = Math.max(maxSide,k+1);
                        }else{
                            break;
                        }

                    }

                }
            }
        }
        return maxSide*maxSide;
    }

}

2.动态规划
在这里插入图片描述
在这里插入图片描述

class Solution {
     // 接着   使用动态规划
    public static int maximalSquare(char[][] matrix) {

        // 首先第一种情况 判空
        int maxSize =0;
        if(matrix == null||matrix.length==0||matrix[0].length==0){
            return maxSize;
        }

        // 接着使用动态规划
        int row = matrix.length;
        int cols = matrix[0].length;
        int[][] dp = new int[row][cols];

        for(int i=0;i<row;i++){
            for(int j=0;j<cols;j++){
                if(matrix[i][j]=='1'){
                    if(i==0||j==0){
                        dp[i][j] = 1;
                    }else{
                        dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                    }
                }
                
                maxSize = Math.max(maxSize,dp[i][j]);
            }
        }
        return maxSize*maxSize;
    }

}

279. 完全平方数

在这里插入图片描述
1.动态规划方程:dp[i] = MIN(dp[i], dp[i - j * j] + 1)

class Solution {
    public int numSquares(int n) {

        int[] dp = new int[n+1];
        for(int i=1;i<=n;i++){
            int minSize = Integer.MAX_VALUE;
            for(int j=1;j*j<=i;j++){
                minSize = Math.min(minSize,dp[i-j*j]);
            }
            dp[i] = minSize+1;
        }
        return dp[n];
    }
}

/*
dp[i] = MIN(dp[i], dp[i - j * j] + 1)
*/

300. 最长递增子序列

在这里插入图片描述
1.动态规划
在这里插入图片描述

class Solution {
    public int lengthOfLIS(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = 1;
       int maxSize = 1;
        for(int i=1;i<nums.length;i++){
            dp[i] = 1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i]=  Math.max(dp[i],dp[j]+1);
                }
            }
            maxSize= Math.max(maxSize,dp[i]);
        }
        return maxSize;
    }
}

309. 最佳买卖股票时机含冷冻期

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

1.动态规划

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        
        int n = prices.length;
        //  定义动态规划方程
        int[][] dp = new int[n][3];

        dp[0][0] = -prices[0];

        for(int i =1;i<prices.length;i++){
            // 第i天持有股票
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][2]-prices[i]);

            // 第i天不持有股票,在冷冻期
            dp[i][1] = dp[i-1][0]+prices[i];

            // 第i天不持有股票,不在冷冻期
            dp[i][2] = Math.max(dp[i-1][2],dp[i-1][1]);

        }

        return Math.max(dp[n-1][1],dp[n-1][2]);
    }
}

2.动态规划优化版本

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }

        int f0 = -prices[0];
        int f1 = 0;
        int f2 = 0;

        int n = prices.length;
        for(int i=1;i<n;i++){

            // f0代表第i天持有股票
            int newf0 = Math.max(f0,f2-prices[i]);

            // f1代表第i天不持有股票,在冷冻期
            int newf1 = f0 + prices[i];

            // f2代表第i天不持有股票,不在冷冻期
            int newf2 = Math.max(f1,f2);


            // 滚动数据一直赋值
            f0= newf0;
            f1= newf1;
            f2= newf2;
        }

        return Math.max(f1,f2);
    }
}

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:

输入:coins = [2], amount = 3
输出:-1
示例 3:

输入:coins = [1], amount = 0
输出:0

在这里插入图片描述

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];

        Arrays.fill(dp,amount+1);

        dp[0] = 0;
        for(int i=1;i<=amount;i++){
            for(int j=0;j<coins.length;j++){
                if(i>=coins[j]){
                    dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);
                }
            }
        }

        return dp[amount]==amount+1?-1:dp[amount];

    }
}

337. 打家劫舍 III

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
在这里插入图片描述
在这里插入图片描述

1.暴力破解方法

在解法一和解法二中,我们使用爷爷、两个孩子、4 个孙子来说明问题
首先来定义这个问题的状态
爷爷节点获取到最大的偷取的钱数呢

首先要明确相邻的节点不能偷,也就是爷爷选择偷,儿子就不能偷了,但是孙子可以偷
二叉树只有左右两个孩子,一个爷爷最多 2 个儿子,4 个孙子
根据以上条件,我们可以得出单个节点的钱该怎么算
4 个孙子偷的钱 + 爷爷的钱 VS 两个儿子偷的钱 哪个组合钱多,就当做当前节点能偷的最大钱数。这就是动态规划里面的最优子结构

由于是二叉树,这里可以选择计算所有子节点

4 个孙子投的钱加上爷爷的钱如下
int method1 = root.val + rob(root.left.left) + rob(root.left.right) + rob(root.right.left) + rob(root.right.right)
两个儿子偷的钱如下
int method2 = rob(root.left) + rob(root.right);
挑选一个钱数多的方案则
int result = Math.max(method1, method2);
将上述方案写成代码如下

public int rob(TreeNode root) {
    if (root == null) return 0;

    int money = root.val;
    if (root.left != null) {
        money += (rob(root.left.left) + rob(root.left.right));
    }

    if (root.right != null) {
        money += (rob(root.right.left) + rob(root.right.right));
    }

    return Math.max(money, rob(root.left) + rob(root.right));
}

2.动态规划方法

在这里插入图片描述
表示为公式如下

root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) + Math.max(rob(root.right)[0], rob(root.right)[1])
root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        int[] result = robInternel(root);
        return Math.max(result[0],result[1]);
    }

    public int[] robInternel(TreeNode root){

        int[] result = new int[2];
        if(root==null){
            return result;
        }

        int[] left = robInternel(root.left);
        int[] right = robInternel(root.right);

        // 不偷
        result[0] = Math.max(left[0],left[1])+Math.max(right[0],right[1]); 

       // 偷
        result[1] = root.val + left[0] + right[0];

       return result;

    }
}

416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

1.动态规划

  • 首先判断数组数量小于2,返回false
  • 数组的和为奇数,返回false
  • 数组最大元素>数组的和除2,返回false
  • 如果数组和为偶数,并且从0-i中能找到和为target的则说明可以
class Solution {
    public boolean canPartition(int[] nums) {
        // 数组数量小于2的话,肯定不行
        if(nums.length<2){
            return false;
        }

        int sum =0;
        int maxNum = nums[0];
        for(int i=0;i<nums.length;i++){
            sum += nums[i];
            maxNum = Math.max(nums[i],maxNum);
        }

        // 如果是奇数,则不行
        if(sum%2==1){
            return false;
        }

        int n = nums.length;
        int target = sum/2;

        if(maxNum>target){
            return false;
        }

        
        boolean[][] dp = new boolean[n][target+1];

        // 默认数据赋值
        for(int i=0;i<nums.length;i++){
            dp[i][0] = true;
        }

        dp[0][nums[0]] = true;

        for(int i=1;i<n;i++){
            int num = nums[i];
            for(int j=1;j<=target;j++){
                if(j>=num){
                    dp[i][j] = dp[i-1][j]||dp[i-1][j-num];
                }else{
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n-1][target];


    }
}

2.动态规划优化之后
在这里插入图片描述

class Solution {
    public boolean canPartition(int[] nums) {
        // 数组数量小于2的话,肯定不行
        if(nums.length<2){
            return false;
        }

        int sum =0;
        int maxNum = nums[0];
        for(int i=0;i<nums.length;i++){
            sum += nums[i];
            maxNum = Math.max(nums[i],maxNum);
        }

        // 如果是奇数,则不行
        if(sum%2==1){
            return false;
        }

        int n = nums.length;
        int target = sum/2;

        if(maxNum>target){
            return false;
        }


       boolean[] dp = new boolean[target+1];

       dp[0] = true;
       for(int i=0;i<n;i++){
           int num = nums[i];
           for(int j=target;j>=num;j--){
               dp[j]|=dp[j-nums[i]];
           }
       }
       return dp[target];

    }
}

494. 目标和

在这里插入图片描述
1.回朔法统计

在这里插入图片描述

class Solution {
    public int count= 0;
    public int findTargetSumWays(int[] nums, int target) {
        // 回溯
        findTargetSumWayss(nums,target,0,0);
        return count;
    }

    public void findTargetSumWayss(int[] nums, int target,int index,int sum) {
        if(index==nums.length){
            if(sum==target){
                count++;
            }
        }else{
            findTargetSumWayss(nums,target,index+1,sum+nums[index]);
            findTargetSumWayss(nums,target,index+1,sum-nums[index]);
        }

    }

}

2.动态规划(转换为背包问题)
在这里插入图片描述

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for(int num:nums){
            sum+=num;
        }

        if((target+sum)%2==1){
            return 0;
        }


        if(sum<Math.abs(target)){
            return 0;
        }

        int w = (target+sum)/2;

        
        int[] dp = new int[w+1];
        dp[0]=1;
        for(int num:nums){
            for(int j=w;j>=num;j--){
                dp[j] += dp[j-num];
            }
        } 
        return dp[w];

    }
}

647. 回文子串

在这里插入图片描述
1.暴力破解方法

class Solution {
    public int countSubstrings(String s) {
        int count = 0;
        for(int j=1;j<=s.length();j++){
            for(int i=0;i<j;i++){
                if(checkString(s.substring(i,j))){
                    count++;
                }
            }
        }
        return count;
    }


    // 首先使用暴力破解
    public boolean checkString(String s){
        if(s==null||s.length()==0){
            return true;
        }
        
        char[] chars = s.toCharArray();
        for(int i=0;i<=chars.length/2;i++){
            if(chars[i]!=chars[chars.length-1-i]){
                return false;
            }
        }

        return true;
    }
}

2.动态规划
在这里插入图片描述

class Solution {
    public int countSubstrings(String s) {

        if(s==null||s.length()==0){
            return 0;
        }

        int n = s.length(); 

        boolean[][] dp = new boolean[n][n];

        int result = 0;
        for(int j=0;j<n;j++){
            for(int i=0;i<=j;i++){
                if(s.charAt(i)==s.charAt(j)){
                    if(j-i<=1){
                       result++;
                       dp[i][j] = true; 
                    }else if(dp[i+1][j-1]){
                       result++;
                       dp[i][j] = true;
                    }
                }
                
            }
        }

        return result;


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值