求职Leetcode有关动态规划的题目(1)

1.最长有效括号 

 

 这道题我们首先可以用栈来解决,当“(”进入时,将其压入,当到“)”的时候,我们检查栈是否为空,如果不为空,那说明里面有"(",那我们将其弹出进行匹配,然后再进行判断,如果是空的,那说明全部匹配完成,我们直接更新最大的ans有效括号长度,如果不为空,那么说明这里还不是最大有效匹配位置呗,就再更新它目前的位置就好了,当我们压入“)”,发现是空,那我们start更新到这个位置,继续往后判断是否存在更长的有效括号。

class Solution {
    public int longestValidParentheses(String s) {
      Stack<Integer> st =new Stack<Integer>();
      int ans=0;
      for(int i =0,start=0;i<s.length();i++){
        if(s.charAt(i)=='('){
            st.push(i);
        }else{
            if(!st.isEmpty()){
                st.pop();
                if(st.isEmpty()){
                     ans=Math.max(ans,i-start+1);
                }
                else{
                    ans = Math.max(ans,i - st.peek());
                } 
            }else start =i+1;
        }
      }
      return ans;
    }
}

 这道题目主要还是想介绍用动态规划来写,首先动态规划的思想是,分成子问题来求解,那么首先我们先推导出:

s[i]=‘)’ 且 s[i−1]=‘(’,也就是字符串形如 “……()”,我们可以推出:

dp[i]=dp[i−2]+2

 

上面是第一个子问题,接下来我们放大情况: 

 类似于这种……((……)),我们看一下下面的图来看下,所以我们要判断第i -1- dp[i - 1]个字符是否是"(",如果是,那么递推公式是dp[i]=dp[i - 1] + 2 + dp[i - dp[i - 1] - 2],这里dp[i - dp[i - 1] - 2]是第一个省略号构成的有效括号,这个不要忘了

 

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

2.接雨水 

这道题的话,我们首先可以用双指针的方法来求解,我们定义两个指针left和right,都只能往中间的方向靠拢,leftmax和rightmax分别用来记录目前遇到的left和right经过的位置下最大高度。

我们要理解为什么能接到雨水,是因为有凹字形的中间小缺口才能装到水,那这需要我们双指针在遍历高度时,能找到这样的高度差,left和right只要形成了高度差,我们不用管中间是个什么情况,我们只要将当前已知单方向上知道的最大高度减去当前高度,求出目前单位的水位差就OK了。下面是整体的步骤:

维护两个指针 left 和 right,以及两个变量 leftMax 和 rightMax,初始时

left=0,right=n−1,leftMax=0,rightMax=0。

指针 left 只会向右移动,指针 right 只会向左移动,在移动指针的过程中维护两个变量 leftMax 和 rightMax 的值。

  1. 使用 height[left] 和 height[right] 的值更新 leftMax 和 rightMax 的值;
  2. 如果 height[left]<height[right],则必有 leftMax<rightMax,下标 left 处能接的雨水量等于 leftMax−height[left],将下标 left 处能接的雨水量加到能接的雨水总量,然后将 left 加 1(即向右移动一位);
  3. 如果 height[left]≥height[right],则必有 leftMax≥rightMax,下标 right 处能接的雨水量等于 rightMax−height[right],将下标 right 处能接的雨水量加到能接的雨水总量,然后将 right 减 1(即向左移动一位)。

       

class Solution {
    public int trap(int[] height) {
        int ans =0;
        int left =0,right =height.length-1;
        int leftMax=0,rightMax =0;
        while(left<right){
            leftMax=Math.max(leftMax,height[left]);
            rightMax=Math.max(rightMax,height[right]);
            if(height[left]<height[right]){
                ans+=leftMax-height[left];
                left++;
            }else{
                ans +=rightMax-height[right];
                right--;
            }
        }
        return ans;
   
    }
}

常规方法讲完,让我们来看看动态规划的解法: 

其实思路是差不多的,也是看左右两边遍历的到的高度,取最小的值,然后减去当前的高度。

leftMax[0]=height[0],rightMax[n−1]=height[n−1]。两个数组的其余元素的计算如下:

  • 当 1≤i≤n−1 时,leftMax[i]=max(leftMax[i−1],height[i]);
  • 当 0≤i≤n−2 时,rightMax[i]=max(rightMax[i+1],height[i])。

因此可以正向遍历数组 height 得到数组 leftMax 的每个元素值,反向遍历数组 height 得到数组 rightMax 的每个元素值。

在得到数组 leftMax 和 rightMax 的每个元素值之后,对于 0≤i<n,下标 i 处能接的雨水量等于 min(leftMax[i],rightMax[i])−height[i]。遍历每个下标位置即可得到能接的雨水总量。 

 

class Solution {
    public int trap(int[] height) {
        int n= height.length;
        if(n==0){
            return 0;
        }
        int[] leftMax =new int[n];
        leftMax[0]=height[0];
        for(int i=1;i<n;i++){
            leftMax[i]=Math.max(leftMax[i-1],height[i]);
        }
        int[] rightMax =new int[n];
        rightMax[n-1]=height[n-1];
        for(int i =n-2;i>=0;i--){
            rightMax[i]=Math.max(rightMax[i+1],height[i]);
        }
        int ans =0;
        for(int i =0;i<n;i++){
            ans+=Math.min(leftMax[i],rightMax[i])-height[i];
        }
        return ans;
      
    }
}

3.编辑距离

 看评论区说这道题是很多大厂的笔试题,像字节,滴滴等一些公司都考到了,还是要认真对待这个动态规划的题目的。

思路:
动态规划

定义 dp[i][j],dp[i][j] 代表 word1 中前 i 个字符,变换到 word2 中前 j 个字符,最短需要操作的次数

需要考虑 word1 或 word2 一个字母都没有,即全增加/删除的情况,所以预留 dp[0][j] 和 dp[i][0]
状态转移
增,dp[i][j] = dp[i][j - 1] + 1

删,dp[i][j] = dp[i - 1][j] + 1
改,dp[i][j] = dp[i - 1][j - 1] + 1

按顺序计算,当计算 dp[i][j] 时,dp[i - 1][j] , dp[i][j - 1] , dp[i - 1][j - 1] 均已经确定了
配合增删改这三种操作,需要对应的 dp 把操作次数加一,取三种的最小
如果刚好这两个字母相同 word1[i - 1] = word2[j - 1] ,那么可以直接参考 dp[i - 1][j - 1] ,操作不用加一

 

 

 

 

class Solution {
    public int minDistance(String word1, String word2) {
        int n1 = word1.length();
        int n2 = word2.length();
        int[][] dp = new int[n1 + 1][n2 + 1];
        // 第一行
        for (int j = 1; j <= n2; j++) dp[0][j] = dp[0][j - 1] + 1;
        // 第一列
        for (int i = 1; i <= n1; i++) dp[i][0] = dp[i - 1][0] + 1;

        for (int i = 1; i <= n1; i++) {
            for (int j = 1; j <= n2; j++) {
                if (word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1];
                else dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
            }
        }
        return dp[n1][n2];  
    }
}   

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值