Leecode题解:longest-valid-parentheses

原题网址:https://leetcode.com/problems/longest-valid-parentheses/
难度:Hard

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 “()()”

题目给定一个由若干圆括号组成的序列,要求从中找出最长的合法子序列。相比找出最长的合法子序列,要判断一串圆括号序列是否合法并不是很难,一种较为简单的做法是通过栈来实现。从左至右逐个读取括号,若为左括号则压入栈,若为右括号则判断栈是否为空,若为空则说明该右括号无法匹配到左括号,若不为空则弹出栈顶(入栈的都是左括号)继续读入下一个括号,直到所有括号都读完后栈为空则说明序列合法。复杂度为O(n)。

所以有一种做法是遍历所有的(i,j)(0 <= i < j < len),判断子串str[i:j+1]是否为合法序列,并记录当前最长的合法序列长度。算法总的复杂度达到 O ( n 3 ) O(n^3) O(n3)

我们还可以试着将该问题分解成子问题,由子问题逐步求解出最终问题。一个最长合法序列如何分步得到呢?一个自然的想法是较短的两个合法序列拼接成更长的序列。对于括号序列,有两种方式从短的合法序列增长成更长的合法序列:一种是嵌套,通过给合法序列外加一层括号得到新的合法序列,另一种是拼接,两个合法的括号序列可以拼接一个更长的合法序列。

既然如此,那我们的最小子问题就可以设计成寻找“()”,这是最短的合法序列。然后再判断该序列能否扩展成"()()“或”(())",以此类推,逐渐找出更长的合法序列。

在算法实现上,我们还需要进一步确定如何记录我们找到的合法匹配的括号,以下给出一种实现(合法匹配的括号在字符串中标记为0,最终在标记好的字符串中寻找最长的连续0序列即可知道最长合法序列的长度):

示例:给定序列'()())((()))'
第一趟遍历字符串:将所有'()'都标记为'\0\0'
得到标记后的字符串'\0\0\0\0)((\0\0))'
在下一趟遍历中'\0'会被忽略,相当于在遍历字符串')(())'(将所有的'\0'去掉)

第二趟遍历字符串(遇到'\0'则跳过):将所有'()'都标记为'\0\0'
得到标记后的字符串'\0\0\0\0)(\0\0\0\0)'
在下一趟遍历中'\0'会被忽略,相当于在遍历字符串')()'(将所有的'\0'去掉)

第三趟遍历字符串(遇到'\0'则跳过):将所有'()'都标记为'\0\0'
得到标记后的字符串'\0\0\0\0)\0\0\0\0\0\0'
在下一趟遍历中'\0'会被忽略,相当于在遍历字符串')'(将所有的'\0'去掉)

第四趟遍历字符串(遇到'\0'则跳过):将所有'()'都标记为'\0\0'
这一趟遍历并未找到任何'()',说明所有能匹配的括号已经匹配完毕,终止循环

最终得到的标记字符串:'\0\0\0\0)\0\0\0\0\0\0'
最长的连续'\0'序列长度为6,对应的括号序列为'((()))',即最长合法序列
由上面的循环过程也可以看出我们是逐层的匹配括号的(从最内层括号开始匹配)

对应于上述过程的代码实现如下:

// leetcode评测成绩: 4ms 排名前56.25%
func longestValidParentheses(s string) int {
    str := []byte(s)
    const left = byte('(')
    const right = byte(')')
    var state int
    // 逐趟匹配括号
    for ;; {
        state = 0
        matched := false
        for i := 0;i < len(str);i++ {
            switch str[i] {
                // 忽略被标记的字符(已匹配的括号)
                case 0:
                    continue
                // 读到左括号则更新state值,记录该左括号的位置
                case left:
                    state = i + 1
                // 读取到右括号则判断在它前面是否有左括号与之配对(如果有则左括号的下标为state-1)
                case right:
                    if state != 0 {
                        str[state - 1] = 0
                        str[i] = 0
                        state = 0
                        matched = true
                    }
            }
            
        }
        if !matched {
            break
        }
    }
    // 计算标记字符串中最长的连续\0序列
    maxLength := 0
    for i := 0;i < len(str);i++ {
        if str[i] != 0 {
            continue
        }
        for j := i + 1;j < len(str);j++ {
            if str[j] == 0 && (j == len(str) - 1 || str[j + 1] != 0) {
                if j - i + 1 > maxLength {
                    maxLength = j - i + 1
                }
                i = j + 1
                break
            }
            continue
        } 
    }
    
    return maxLength
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值