动态规划例题

打算这段时间(我也不清楚具体多久,打算多刷点动态规划的问题)

动态规划

2022.9.29

最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = “babad”

输出:“bab”

解释:“aba” 同样是符合题意的答案。

1.中心扩散法:

先写代码

class Solution {
    public String longestPalindrome(String s) {
        if(s.length()==0||s.length()==1){
            return s;
        }
        else{
       int left ;
        int right;
        int len = 0;
        int max = 0;
        int start = 0;
        int end = 0;
        int i ;
        for( i = 0;i<s.length();i++){
            left = i-1;
            right = i+1;
            while(left>=0&&(s.charAt(left)==s.charAt(i))){
                left--;
                len++;
            }
            while(right<s.length()&&(s.charAt(right)==s.charAt(i))){
                right++;
                len++;
            }
            while(left>=0&&right<s.length()&&(s.charAt(left)==s.charAt(right))){
                len+=2;
                right++;
                left--;
            }
            if(max < len){
                max = len;
                start  = left;
                end = right;
            }
            len = 1;
        }
       return s.substring(start+1,start+max+1);
       
        }
    }
}

我感觉要先说两个api,java好久没写过算法了,忘的差不多了

a.charAt(n)

字符串a中,找到它的第n+1个字符

a.substring(b,c)

只要字符串a中的第b+1个字符到c个字符

然后感觉,与其叫从中心扩散还不如叫暴力枚举法

定义一个i=0,让i的长度小于字符串的长度的循环,每进行一次循环i的大小+1

定义一个left和right,在每次循环的初始分别在i的两侧

后面感觉很容易,没必要写了

2.动态规划:

先写代码

class Solution {
    public String longestPalindrome(String s) {
       if (s == null || s.length() < 2) {
            return s;
        }
        else{
      int start = 0;
      int max = 1 ;
      boolean dp[][] = new boolean[s.length()][s.length()];
      for(int i =1;i<s.length();i++){
          for(int j = 0 ;j<i;j++){
              if((s.charAt(i)==(s.charAt(j)))&&((i-j<=2)||(dp[j+1][i-1]))){
                  dp[j][i]=true;
                    int len = i-j+1;
                    if(len>max){
                        max = len ;
                        start  = j;
                    }
              }
          }
      }
      return s.substring(start,start+max);
    }
}
}

当时候把boolean dp[][] = new boolean[s.length()][s.length()]

写成

Boolean dp[][] = new Boolean[s.length()][s.length()]

然后报错了,还能过几个样例

dp是经典的用空间换时间

和上一个方法一对比就明感觉就讲一下第一个if里面的语句就行了

==就不用说了,说后面的

i-j小于等于2这个好理解

dp[j+1][i-1]这句

比如ababa

已经知道ababa是最长回文串,那你就得证明第一个和最后一个相等且bab是回文串

就能理解了

2022.10.18(10.20补上这道题)

最长有效括号

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:

输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:

输入:s = “”
输出:0

1.动态规划:

先写代码

class Solution {
    public int longestValidParentheses(String s) {
        if(s.length()<=1){
            return 0;
        }
        else{
            int dp[] = new int[s.length()];
            int max = 0;
            for(int i=1;i<s.length();i++){
                if(s.charAt(i)==')'){
                    if(s.charAt(i-1)=='('){
                        dp[i]=(i-2>=0?dp[i-2]:0)+2;
                    }
                  else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                max = Math.max(max, dp[i]);

                }
            }
            return  max;
        }
    }
}

首先这道题,我们要想有哪几种情况会有比较长的括号

第一种:

()()()()

这种情况是最简单的一种情况

我们不妨从i=1开始(这样简单的多,i=0的情况太难了)

因为是从i=1开始,所以s.charAt(i)必须得用’)’

在s.charAt(i)==')'的情况下,如何得到max呢

就必须s.charAt(i-1)==‘(’

把刚才的情况写一下

012345

( ) ( ) ( )

s.charAt(i-1)=='('的情况下,如果i-2<0,则dp[i]=0+2

如果i-2>=0,则dp[i]=(dp[i-2]+2)

第二种:

((()))

这种情况比较难

还是从i=1开始

最开始new的时候全部都是0

0 1 2 3 4 5

( ( ( ) ) )

0 0 0 0 0 0

到i=3的时候,dp[3]=dp[1]+2 = 0+2 = 2

但是呢,当i=4的时候,dp[3]=')'而不是(

所以dp[4]仍然为0吗,nonono

所以要改变条件为

若i-dp[i-1]-1==‘(’

依然用上面的这个例子:

0 1 2 3 4 5

( ( ( ) ) )

0 0 0 2 ? ?

i=4时,s.charAt(i-dp[3]-1)==‘(’

所以i=4的**‘)‘可以和i=1的’(’**匹配

原本我以为条件就是这个,但是呢,如果出现这种情况:

0 1 2

( ) )

0 2 ?

当i=2的时候,i-dp[1]-1=2 - 2- 1 =-1

else if中会直接报错

所以前面必须再加一个条件 i-dp[i-1]-1>=0

综合得出第二个条件

(i - dp[i-1]-1>=0)&&(s.charAt(i-dp[i-1]-1)==‘(’)

条件是得出来了,dp应该怎么算呢?

首先,一定是和dp[i-1]有关

dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;

2022.10.19(10.21补上这道题)

跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

1.贪心算法:

先写代码

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

来举个例子:

[2, 3, 1, 1, 4]

我们一开始在位置 00,可以跳跃的最大长度为 22,因此最远可以到达的位置被更新为 22;

我们遍历到位置 11,由于 1≤2,因此位置 11 可达。我们用 11 加上它可以跳跃的最大长度 33,将最远可以到达的位置更新为 44。由于 44 大于等于最后一个位置 44,因此我们直接返回 True。

这时我们再看看代码

为什么是i<=max,因为只有当i<=max时,i-max的这块区间他都可以取到,而其他的区间却不行

max(i+nums[i],max)是为了确定到底是现在的这个位置远还是原来的位置远

2.动态规划:

2022.10.20(10.21补上这道题)

跳跃游戏 II

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 1000
1.动态规划:
class Solution {

  public int jump(int[] nums) {int dp[] = new int[nums.length];int min = 10000;int length = 0;for(int i = 1;i<nums.length;i++){

​      dp[i] = min;}for(int i=1;i<nums.length;i++){for(int j = 0;j<i;j++){if(i<=(j+nums[j])){

​          dp[i]=Math.min(dp[j]+1,dp[i]);

​          length = i;}}}return dp[length];

  }

}

也还是先举个例子吧

[2, 3, 1, 1, 4]

index:0 1 2 3 4

2 3 1 1 4

​ dp: 0 ? ? ? ?

反正dp[0]为0没什么争议的,剩下的dp[1]到dp[nums.length-1]把它们设置为大一点的值,可以把它们设置为nums.length也可以把它们设置为10000

index:0 1 2 3 4

2 3 1 1 4

​ dp: 0 ? ? ? ?

nums[0]可以直接跳到nums[1]和nums[2]

更新:

index:0 1 2 3 4

2 3 1 1 4

​ dp: 0 1 1 ? ?

到nums[1],它可以跳到nums[2],nums[3],nums[4]

更新:

index:0 1 2 3 4

2 3 1 1 4

​ dp: 0 1 1 2 2

注意dp[2]仍然是1而不是2,(1<2这是常识)

后面就不用看了,肯定比2大,

所以最小就是2

2.贪心算法:

代码

   if(nums.length==1){
            return 0;
        }
        else{
        int w = nums.length-1;
        int step = 0;
        int i = 0;
        boolean a = true;
        while(a){
            if(nums[i]+i<w){
                i++;
            }
            else{
                int sum = nums[i]+i;
                step++;
                w = i;
                if(w!=0){
                    i=0;
                }
                else{
                    a = false;
                }
            }
        }
       return step;

        }

分两种情况,第一种当nums只有一个元素的情况下,直接return 0

第二种情况,nums中的元素大于1,找出最远的能刚好跳出去的位置,把这个位置定义为w,把i的值赋给w,运行的step+1,然后再看在w之前哪个i能最远到达w,最终得到step

2022.10.21

最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
1.动态规划:
class Solution {
    public int maxSubArray(int[] nums) {
            int dp[] = new int[nums.length];;
            int max = nums[0];
            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];
              }
          }
          for(int i = 0; i < nums.length;i++){
             max = max>dp[i]?max:dp[i];
          }
          return max;
    }
}

感觉是一种状态转移,感觉看代码就可以理解,但还是举一个例子吧

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 ? ? ? ? ? ? ? ?

index为1时

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 ? ? ? ? ? ? ?

index 为2

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 ? ? ? ? ? ?

index 为3

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 2 ? ? ? ? ?

index 为4

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 2 1 ? ? ? ?

index 为5

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 2 1 3 ? ? ?

index 为6

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 2 1 3 4 ? ?

index 为7

index:0 1 2 3 4 5 6 7 8

​ -2,1,-3,4,-1,2,1,-5,4

dp: -2 1 -2 2 1 3 4 -1 ?

index 为8

index:0 1 2 3 4 5 6 7 8

​ -2, 1,-3, 4, -1,2,1,-5, 4

dp: -2 1 -2 2 1 3 4 -1 4

所以最大就是4

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值