keep笔试 -有效括号:栈,DP,正向逆向结合法(最大乘积子数组)

1. 有效的括号

方法一:栈

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

class Solution:
    def isValid(self, s: str) -> bool:
        if len(s) % 2 != 0:
            return False
        pairs = {
            ')': '(',
            ']': '[',
            '}': '{'
        }
        stack = []
        for char in s:
            if char in pairs:
                if not stack or stack[-1] != pairs[char]:
                    return False
                stack.pop()
            else:
                stack.append(char)
        return not stack

2. 最长有效括号

方法一:栈

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

视频中的方法三:https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
大约从11分钟开始

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s: return 0

        stack = []
        ans = 0
        for i in range(len(s)):
            # 入栈条件
            if not stack or s[i] == '(' or s[stack[-1]] == ')':
                stack.append(i)     # 下标入栈
            else:
                # 计算结果
                stack.pop()
                ans = max(ans, i - (stack[-1] if stack else -1))
        return ans



方法二:动态规划

时间复杂度O(n),空间复杂度O(n)
1)解题思路:
动态规划,dp[i]表示以索引i为结尾的最长有效括号字串的长度。

如果s[i] == ‘(’,则以i结尾的字符串不可能是有效括号,dp[i] = 0。
如果s[i] == ‘)’,有以下情况:
s[i-1] == ‘(’,则dp[i] = dp[i-2] + 2
s[i-1] == ‘)’,则dp[i] = dp[i-1] + 2 + dp[i - dp[i-1] - 2]

2)详细解释:

  1. dp数组意义:dp[i] 表示以下标 ii 字符结尾的最长有效括号的长度

  2. 状态转移:
    如果s[i] == ‘(’,则以i结尾的字符串不可能是有效括号,dp[i] = 0。
    1)字符串形如 “……()”,如果第i-1号和第i号是左括号和右括号
    因为结束部分的 “()” 是一个有效子字符串,并且将之前有效子字符串的长度增加了 2 。
    在这里插入图片描述
    2)字符串形如 “……))”,如果第i号和第i-1号都是右括号“)”
    在这里插入图片描述
    dp[i-1]是一些有效括号的长度,因此和i搭配成新的有效括号组合的是i-dp[i-1]-1,所以+2,在i-dp[i-1]-1之前可能还有有效括号长度,所以加dp[i-dp[i-1]-2].
    在这里插入图片描述
    3)代码

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s:
            return 0
        dp = [0] * len(s)
        for i in range(1, len(s)):
            if s[i] == ')':
                if s[i-1] == '(':
                    dp[i] = (dp[i-2] if i >= 2 else 0) + 2
                else:
                    if i - dp[i-1] - 1 >= 0 and s[i - dp[i-1] - 1] == '(':
                        dp[i] = dp[i-1] + 2 + (dp[i - dp[i-1] - 2] if i - dp[i-1] - 2 >= 0 else 0)
        return max(dp)


public 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] >= 1 && 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;
    }
}

在这里插入图片描述
对应思考1:
s.charAt(i - dp[i - 1] - 1) == '('

对应思考2:
i - dp[i - 1] >= 1 # i - dp[i - 1]-1不能 == -1,这样就越界了,找到的是数组倒数第一个元素是否是“(”,滥竽充数了

对应思考3:
在这里插入图片描述
如果i - dp[i-1] - 2 == -1, python中会去找数组倒数第一个元素dp[i - dp[i - 1] - 2] = dp[-1], dp[-1]最后一个更新所以在计算前面的dp[i]时,可以用dp[-1],不影响正确答案;但java不行,负数下标会报错。c不会报错,但会指向第0个元素的地址 - 1,这个地址存储的数字不确定是啥。因此要写成:
((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0)

方法三:正向逆向结合法

n:字符串长度
left:左括号个数
right:右括号个数
maxlength:有效括号子串最大长度

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        n, left, right, maxlength = len(s), 0, 0, 0
        for i in range(n):
            if s[i] =='(':
                left+=1
            else:
                right+=1
            if left == right:
                maxlength = max(maxlength, 2 * right)
            elif right > left:
                left = right = 0
        left = right = 0
        for i in range(n-1,-1,-1):
            if s[i] =='(':
                left+=1
            else:
                right+=1
            if left == right:
                maxlength = max(maxlength, 2 * left)
            elif right < left:
                left = right = 0

        return maxlength

3. 最大乘积子数组

方法一:DP
https://leetcode-cn.com/problems/maximum-product-subarray/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by–36/

我们先定义一个数组 dpMax,用 dpMax[i] 表示以第 i 个元素的结尾的子数组,乘积最大的值,也就是这个数组必须包含第 i 个元素。

那么 dpMax[i] 的话有几种取值:
在这里插入图片描述
如果A[i]为0,那么maxDP和minDP都为0,我们需要从A[i + 1]重新开始。

public int maxProduct(int[] nums) {
    int n = nums.length;
    if (n == 0) {
        return 0;
    } else if (n == 1) {
    	return nums[0];
    }
    int dpMax = nums[0];
    int dpMin = nums[0];
    int max = nums[0];
    for (int i = 1; i < n; i++) {
        //更新 dpMin 的时候需要 dpMax 之前的信息,所以先保存起来
        int preMax = dpMax;
        dpMax = Math.max(dpMin * nums[i], Math.max(dpMax * nums[i], nums[i]));
        dpMin = Math.min(dpMin * nums[i], Math.min(preMax * nums[i], nums[i]));
        max = Math.max(max, dpMax);
    }
    return max;
}


方法二:正向逆向结合法

https://leetcode-cn.com/problems/maximum-product-subarray/solution/python5xing-bu-tong-yu-hui-su-dpde-tricksjie-fa-by/

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        nums_reverse = nums[::-1]
        for i in range(1, len(nums)):
            nums[i] *= nums[i - 1] or 1 # or 1的作用是,当nums[i - 1]==0时,nums[i]乘等自身
            nums_reverse[i] *= nums_reverse[i - 1] or 1
        return max(max(nums),max(nums_reverse))


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值