• 32. Longest Valid Parentheses


    """
    32. Longest Valid Parentheses
    Hard
    1343
    68


    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 "()()"
    """

        今天写leetcode的第32题,题目的大概意思是一个字符串s,是由"(",")"两种字符组成的,问其中最长的左右配对的子字符串是多长。诸如()(()),(()()),这样的都是匹配的。

        这道题目想了很久才做出来。

        从左到右遍历,用一个栈preleftlist来记载"("的数量,用另一个栈tmplist记载连续子字符串的数量,为什么要用两个栈呢,用maxlen记载最长子串长度,用tmp从左到右遍历考虑到这样的情况"()(()"这个的最大子串长度是2,但是如果在最右边再加上一个")"的话就会变成"()(())"这样的话就是6个,也就是最前面的2个可能还有用,所以要用栈来存储数量的信息,具体操作是这样子的从右向左遍历,preleft来记载连续的"("的数量,将preleft进栈到preleftlist栈中,继续遍历,接下来是连续的")",每当有一个")"那么栈preleftlist的顶端的数字就-1,同时tmp+=2;一直如此,直到遇见"(",就将此时的tmp进栈到tmplist中,同时更新maxlen;在此过程中遇见栈preleftlist顶端的数字变成0的话,preleftlist出栈,同时tmplist如果存在栈首就出栈加到tmp上面,这就相当于将原先被"("隔离的两部分加在了一起;如果遇见preleftlist为空的情况的话,说明这个")"找不到可以配对的"("的,此时更新maxlen后就将所有栈清空;代码如下:

    class Solution:
        def longestValidParentheses(self, s):
            """
            :type s: str
            :rtype: int
            """
            s_len = len(s)
            preleftlist = []
            preleft = 0
            maxlen = 0
            tmp = 0
            tmplist = []
            i = 0
            while i < s_len:
                while i < s_len and s[i] == "(":
                    preleft += 1
                    i += 1
                if preleft:
                    preleftlist.append(preleft)
                    preleft = 0
                while i < s_len and s[i] == ")":
                    if preleftlist:
                        tmp += 2
                        preleftlist[-1] -= 1
                        #这一层的左括号已经用完了
                        if not preleftlist[-1]:
                            preleftlist.pop()
                            #如果前面成对的()就把数量合并
                            if tmplist:
                                tmp += tmplist[-1]
                                tmplist.pop()
                    #这个else说明右括号已经找不到可以匹配的左括号了
                    else:
                        maxlen = max(tmp, maxlen)
                        tmp = 0
                        tmplist = []
                    i +=1
                #之前的循环中右括号找到的tmp计算进去
                if tmp:
                    tmplist.append(tmp)
                    maxlen = max(tmp, maxlen)
                tmp = 0
            return max(tmp, maxlen)

        在评论区看到一个很有意思的c++的空间复杂度是O(1)的算法:

        int longestValidParentheses(string s) {
            calcInvalid(s, '(');
            reverse(s.begin(), s.end());
            return calcInvalid(s, ')');
        }
        
        int calcInvalid(string& s, char plus) {
            int stack_ = 0, invalid = 0, longest = 0, length = 0;
            for (int i = 0; i < s.size(); i++) {
                if (s[i] == plus) {
                    stack_++;
                } else {  
                    if (s[i] == '#' || stack_ == 0) {
                        s[i] = '#';
                        stack_ = 0;
                        length = 0;
                        continue;
                    }
                    stack_--;
                }
                
                longest = max(longest, ++length);
            }
            return longest;
        }

        看了一会才看懂是什么意思,对于字符串s,首先考虑到这几点:

    1,每一个右括号要么不配对要么只有唯一的"("与之相对应;

    2,从前遍历,和从后遍历本无本质区别

    3,每一个"("要么不配对,要么只有唯一的")"与之相对应;

    这个算法,我把它分成3段:

    1, 基于这样的想法,首先从前向后遍历,有preleft记录"("的数量,遍历到右括号如果preleft>0就将preleft-1,同时说明这个右括号是有配对的,如果preleft==0说明这个右括号是不配对的,将这个右括号改成其他的某个字符,这样就可以找到所有不配对的")";

    2, 再从后先前遍历找到所有不匹配的"(",再次改为其他字符,这样剩下的字符都是遍历的了

    3,找到字符串中连续的只包含"("或者")"的最长子字符串的长度

    步骤1和步骤2就只是从前遍历和从后遍历的区别可以写成一个函数,3完全可以在步骤2中进行统计,这就是为什么c++只有一个函数的原因

    可惜的是因为python中str是不可变的,只能先转化为list所以空间复制度就不是O(1)了,下面是我写的python代码:

    class Solution:
        def longestValidParentheses(self, s):
            """
            :type s: str
            :rtype: int
            """
            s_len = len(s)
            maxlen = 0
            tmp = 0
            preleft = 0
            lastright = 0
            s = list(s)
            for i in range(s_len):
                if s[i] == "(":
                    preleft += 1
                else:
                    if preleft:
                        preleft -= 1
                    else:
                        s[i] = "@"
                        preleft = 0
            # for i in range(s_len-1,-1,-1):
            #     if s[i] == ")" or s[i] == "@" :
            #         lastright += 1
            #     else:
            #         if lastright:
            #             lastright -= 1
            #         else:
            #             s[i] = "@"
            #             lastright = 0
            for i in range(s_len-1,-1,-1):
                if s[i] == ")":
                    lastright += 1
                elif(s[i]=="@"):
                    lastright = 0
                else:
                    if lastright:
                        lastright -= 1
                    else:
                        s[i] = "@"
                        lastright = 0
            for i in range(s_len):
                if s[i] != "@":
                    tmp += 1
                else:
                    maxlen = max(maxlen, tmp)
                    tmp = 0
            return max(maxlen, tmp)

    注释部分的代码和下面的是可以互换的,都是步骤2的实现

    听说还可以用动态规划做,我等等试着写一下

  • 相关阅读:
    状态模式
    策略模式Strategy(对象行为型)
    模板方法模式
    Java_观察者模式(Observable和Observer) -转
    Cordova自定义插件
    MongoVUE查询备忘
    C#关于HttpClient的统一配置(一)
    C#邮件收发
    WebApi统一输出接口
    移动开发兼容性问题及性能优化
  • 原文地址:https://www.cnblogs.com/mangmangbiluo/p/10091439.html
Copyright © 2020-2023  润新知