LeetCode 32. Longest Valid Parentheses(最长有效括号)

原题

Given a string containing just the characters ‘(’ and ‘)’, find the length of the longest valid (well-formed) parentheses substring.

题目:
给定一个只包含字符 “(’ 和 ')” 的字符串, 查找最长的有效 (格式正确) 圆括号子字符串的长度。

Example 1:

Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"

Example 2:

Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"

My Solution

方案一(错误)

class Solution_four:
    def longestValidParentheses(self, s):
        """
        :type s: str
        :rtype: int
        """
        if not s:
            return 0
        if len(s) <= 1:
            return 0
        res = 0

        list_s = list(s)
        while True:
            mark = True
            for i in range(1, len(list_s)):
                if list_s[i - 1] == '(' and list_s[i] == ')':
                    res += 1
                    # list_s.pop(i - 1)
                    # list_s.pop(i)
                    del list_s[i], list_s[i-1]

                    mark = False
                    break
                else:
                    pass
            if mark:
                break
        return res
  • 忽略了()(()情况,即只考虑了有多少个()没有考虑有效性,所以得出上面这种情况为2,但实际应该为1;
  • list删除元素有三种方式,remove(), del, pop(),其中pop的参数为列表索引值,每次只能pop一个;而del直接这对list元素,后可接多个;
  • 上述代码之所以将删除方式由pop转换为del,是因为list_s.pop(i - 1)之后,list_s的长度减1,造成list_s.pop(i)已经不再是自己当初所设想的i-1之后的第i个元素了(因为list_s长度发生了变化),并且这种方式很容易造成列表索引超出范围(事实上,自己在这个地方卡了不少时间找原因);
  • 上述代码之所以在mark = False之后加上了break,是为了让其跳出for循环,重新让i从1开始取值;自己之前曾尝试直接赋值i = 1,但事实证明这种方式并不能影响for循环中i的变化,for循环的中的i的取值不会随着for循环中list_s长度变化而变化;(这点要尤其注意)

方案二(错误)

class Solution:
    def longestValidParentheses(self, s):
        """
        :type s: str
        :rtype: int
        """
        if not s:
            return 0
        if len(s) <= 1:
            return 0
        count = count_left = count_right = 0
        mark_list = ['(']
        mark_start = False
        left_index = {}
        right_index = {}
        res = 0
        for i in range(1, len(list_s)):
           if s[i] == '(':
                count_left += 1
                count += 1
                mark_start = True
                left_index[count_left] = i
            if s[i] == ')' and mark_start:
                count_right += 1
                count -= 1
                right_index[count_right] = i
         
        if count_left == 0 or count_right == 0:
            return 0
        if count_left <= count_right:
            res = s[left_index[1]:right_index[count_left]+1]
        else:
            res = s[left_index[count_left - count_right + 1]:right_index[count_right]+1]
        return len(res)
  • 试图采用之前做过的查找有效括号的做法来做,依旧是少考虑了有效性; 即忽略了()(()情况,即只考虑了有多少个()没有考虑有效性,所以得出上面这种情况为2,但实际应该为1;

Reference solution

思路分析:

思路一 :
首先,这个题的总体思路相当于Valid Parentheses这道题的升级版。括号匹配,肯定会想到用栈,但是这道题不同的是要找到最长的有效括号子串,子串在寻找的过程中你无法确定它是不是有效
例如()((),在当前的状态来看是两个有效括号,但是如果继续往后走,有可能是()(()),这样的话两个有效括号就变成了一个。
因此必须能够记录匹配的长度变化,可以采用位置记录。例如()((),需要同时记录中间(的位置起始的位置0,虽然当前子串的长度判断是通过和(位置进行差值得到的,但是一旦这个(被匹配掉,就要和起始位置来进行比较。因此,需要一个变量start来记录有效括号的可能的最早的起始位置。

通过start来记录起始位置,

  • 如果当前为(则将位置入栈;
  • 如果是),则判断当前栈:
    • 如果当前栈为空,说明匹配到了一个无效括号,start从i+1的位置开始
    • 如果当前栈不空,说明需要和栈里的(匹配融合掉,这时再看栈:
      • 如果栈为空,说明左括号全部匹配掉了,就需要用当前位置i - start + 1来更新结果值(max(res, i - start + 1)
      • 如果栈不空,说明干掉了一个左括号,还有多余的左括号,就将最大值更新到这个左括号的位置,即max(res, i - stack[-1] + 1)
class Solution:
    def longestValidParentheses(self, s):
        """
        :type s: str
        :rtype: int
        """
        stack = []
        res = start_index = 0 
        for i, count in enumerate(s):
            if count == '(':
                stack.append(i)    
            else:
                if not stack:
                    start_index = i+1    # invalid match
                else:
                    stack.pop()
                    if not stack:    # totally match all stack
                        res = max(res, i - start_index + 1)
                    else:
                        res = max(res, i - stack[-1])
                    
        return res

反思:

  1. 忽略了题目中有效性要求;
  2. 需要多反思自己答案一中出现错误的情况;
  3. 通过自己解决思路二,尝试运用了dict类型,明白了如何对dict类型进行元素添加:right_index[count_right] = i
  4. 对于多重循环,采用for i in range(len(s))到内层循环会很混乱,不如采用enumerate结构,for i,count in enumerate(len(s))

猜你喜欢

转载自blog.csdn.net/Dby_freedom/article/details/82877072