leetcode之玩转有效括号题系列

今天的leetcode每日一题是1111. 有效括号的嵌套深度。不用看题目,只看标题我们就能拆出几个问题来。
1 什么是有效括号?
2 有效括号如何嵌套?

而括号类题目大部分同学可能在学习栈这一数据结构的时候做过。本文就来按照拆出来的两个问题,系列解读括号题。

有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

前面我们也提到过了学习数据结构栈的时候的配套例题就是有效括号。使用栈结构时,每次遇到开符号,也就是(,[,{时直接入栈,而遇到闭符号时,也就是),],}时,将栈顶元素弹出比对。

  • 若遇到闭符号时,栈中无元素,则不是有效的括号。
  • 若遇到闭符号时,栈顶元素不匹配,则不是有效括号。
  • 所以只对开符号做入栈出栈操作。

Go版本代码

时间复杂度:O(n)
空间复杂度:O(n)
执行用时:0 ms
内存消耗:2 MB

func isValid(s string) bool {
//这两个判断加上丝毫没有影响执行用时和内存消耗
	if s=="" {
        return true
    }
    if s[0]==')' || s[0]==']' ||s[0]=='}' {
        return false
    }
    var c []byte//切片当作栈
    symbol := map[byte]byte{
        ')' : '(',
        ']' : '[',
        '}' : '{',
    }
    for _,value := range s {
        clen := len(c)
        if clen>0 {
            if _,ok := symbol[byte(value)];ok {
                if c[clen-1]==symbol[byte(value)] {
                    c = c[:clen-1]//出栈
                    continue
                }
            }
        }
        c = append(c,byte(value))//入栈
    }
    return len(c)==0
}

有效括号的嵌套深度

有效括号字符串 仅由 “(” 和 “)” 构成,并符合下述几个条件之一:

空字符串
连接,可以记作 AB(A 与 B 连接),其中 A 和 B 都是有效括号字符串
嵌套,可以记作 (A),其中 A 是有效括号字符串
类似地,我们可以定义任意有效括号字符串 s 的 嵌套深度 depth(S):

s 为空时,depth("") = 0
s 为 A 与 B 连接时,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括号字符串
s 为嵌套情况,depth("(" + A + “)”) = 1 + depth(A),其中 A 是有效括号字符串
例如:"","()()",和 “()(()())” 都是有效括号字符串,嵌套深度分别为 0,1,2,而 “)(” 和 “(()” 都不是有效括号字符串。

给你一个有效括号字符串 seq,将其分成两个不相交的子序列 A 和 B,且 A 和 B 满足有效括号字符串的定义(注意:A.length + B.length = seq.length)。

现在,你需要从中选出 任意 一组有效括号字符串 A 和 B,使 max(depth(A), depth(B)) 的可能取值最小。

返回长度为 seq.length 答案数组 answer ,选择 A 还是 B 的编码规则是:如果 seq[i] 是 A 的一部分,那么 answer[i] = 0。否则,answer[i] = 1。即便有多个满足要求的答案存在,你也只需返回 一个。
示例 1:

输入:seq = "(()())"
输出:[0,1,1,1,1,0]

示例 2:

输入:seq = "()(())()"
输出:[0,0,0,1,1,0,1,1]

提示:

1 <= text.size <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-nesting-depth-of-two-valid-parentheses-strings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

相信有了有效括号题目的铺垫,大家应该能较快读懂题目了。在有效括号的基础上,本题增加了嵌套深度这一条件。除了栈这种解题方式,官方题解还很优秀地提供了找规律的解法,推荐大家去看看。

使用栈来解题

下图是从官方题解中找来的,由于本题想要将有效括号串拆分成两个子有效括号串,并且使 max(depth(A), depth(B)) 的可能取值最小,也就是想要两个子串的嵌套深度尽可能相等。

思路是那就把嵌套深度对半分,A串占据嵌套深度为奇数的,B串占据嵌套深度为偶数的。

括号序列   ( ( ) ( ( ) ) ( ) )
下标编号   0 1 2 3 4 5 6 7 8 9
嵌套深度   1 2 2 2 3 3 2 2 2 1 
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-nesting-depth-of-two-valid-parentheses-strings/solution/you-xiao-gua-hao-de-qian-tao-shen-du-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

时间复杂度:O(n)
空间复杂度:O(1)
执行用时:0 ms
内存消耗:3.7 MB

func maxDepthAfterSplit(seq string) []int {
    var ans []int//需要返回的划分方案
    d := 0//嵌套深度
    for _,v := range seq {
        if v == '(' {
            d += 1
            ans = append(ans, 1-d%2)
        } 
        if v == ')' {
            ans = append(ans, 1-d%2)
            d -= 1
        } 
    }
    return ans
}

使用找规律方法解题

括号序列   ( ( ) ( ( ) ) ( ) )
下标编号   0 1 2 3 4 5 6 7 8 9
嵌套深度   1 2 2 2 3 3 2 2 2 1 

还是这个图,观察找规律。

  1. 左括号 ( 的下标编号与嵌套深度的奇偶性相反
  2. 右括号 ) 的下标编号与嵌套深度的奇偶性相同

策略是:

  • 下标编号为奇数的 (,其嵌套深度为偶数,分配给 B;
  • 下标编号为偶数的 (,其嵌套深度为奇数,分配给 A。
  • 下标编号为奇数的 ),其嵌套深度为奇数,分配给 A;
  • 下标编号为偶数的 ),其嵌套深度为偶数,分配给 B。

规律证明:

对于任意一个左括号,下标为X,它之前的左括号有L个,之前的右括号有R个。

那么
X = L+R   //下标编号从0开始
嵌套深度Y为
Y = L-R+1

由于L+R 和L-R奇偶相同,所以下标X和嵌套深度Y奇偶相反。
同理可证右括号的下标和嵌套深度奇偶相同。

时间复杂度:O(n)
空间复杂度:O(1)
执行用时:0 ms
内存消耗:3.7 MB

func maxDepthAfterSplit(seq string) []int {
    var ans []int
    
    for k,v := range seq {
        if v == '(' {
            ans = append(ans, k%2)
        } else {
            ans = append(ans, 1-k%2)
        }
    }
    return ans
}

总结

有效括号的两题做下来有了编程中模块化的思想了。将有效括号的判定变成一个模块,在有了嵌套深度这一新的场景时,可以复用模块+新场景的判断即可。

你可能感兴趣的:(leetcode)