有效括号的嵌套深度

标签: 字符串

难度: Medium

有效括号字符串 定义:对于每个左括号,都能找到与之对应的右括号,反之亦然。详情参见题末「有效括号字符串」部分。

嵌套深度 depth 定义:即有效括号字符串嵌套的层数,depth(A) 表示有效括号字符串 A 的嵌套深度。详情参见题末「嵌套深度」部分。

有效括号字符串类型与对应的嵌套深度计算方法如下图所示:

给你一个「有效括号字符串」 seq,请你将其分成两个不相交的有效括号字符串,A 和 B,并使这两个字符串的深度最小。

  • 不相交:每个 seq[i] 只能分给 AB 二者中的一个,不能既属于 A 也属于 B
  • AB 中的元素在原字符串中可以不连续。
  • A.length + B.length = seq.length
  • 深度最小:max(depth(A), depth(B)) 的可能取值最小。 

划分方案用一个长度为 seq.length 的答案数组 answer 表示,编码规则如下:

  • answer[i] = 0seq[i] 分给 A
  • answer[i] = 1seq[i] 分给 B

如果存在多个满足要求的答案,只需返回其中任意 一个 即可。

示例 1:

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

示例 2:

输入:seq = "()(())()"
输出:[0,0,0,1,1,0,1,1]
解释:本示例答案不唯一。
按此输出 A = "()()", B = "()()", max(depth(A), depth(B)) = 1,它们的深度最小。
像 [1,1,1,0,0,1,1,1],也是正确结果,其中 A = "()()()", B = "()", max(depth(A), depth(B)) = 1 。 

提示:

  • 1 < seq.size <= 10000

有效括号字符串:

仅由 "(" 和 ")" 构成的字符串,对于每个左括号,都能找到与之对应的右括号,反之亦然。
下述几种情况同样属于有效括号字符串:

  1. 空字符串
  2. 连接,可以记作 ABAB 连接),其中 A 和 B 都是有效括号字符串
  3. 嵌套,可以记作 (A),其中 A 是有效括号字符串

嵌套深度:

类似地,我们可以定义任意有效括号字符串 s嵌套深度 depth(S):

  1. s 为空时,depth("") = 0
  2. sAB 连接时,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括号字符串
  3. s 为嵌套情况,depth("(" + A + ")") = 1 + depth(A),其中 A 是有效括号字符串

例如:"""()()",和 "()(()())" 都是有效括号字符串,嵌套深度分别为 0,1,2,而 ")(" 和 "(()" 都不是有效括号字符串。

Submission

运行时间: 27 ms

内存: 16.2 MB

class Solution:
    def maxDepthAfterSplit(self, seq: str) -> List[int]:
        ans = [0] * len(seq)
        x = 0
        for i, c in enumerate(seq):
            if c == "(":
                ans[i] = x & 1
                x += 1
            else:
                x -= 1
                ans[i] = x & 1
        return ans

Explain

该题解使用了贪心策略来尽可能平均地分配左括号和右括号到两个字符串A和B中,以确保它们的最大嵌套深度最小。每遇到一个左括号,根据当前的深度(通过变量x表示,每遇到一个左括号增加1,右括号减少1)的奇偶性,决定将其分配给A(0)还是B(1)。这样做的好处是可以均匀地分配括号,减少任一子字符串的连续深度增加。右括号的处理方式与左括号相同,只是在深度变化前进行分配。

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def maxDepthAfterSplit(self, seq: str) -> List[int]:
        ans = [0] * len(seq)  # 创建一个与seq等长的数组,用于保存答案
        x = 0  # 使用x来跟踪当前的深度(未匹配的左括号的数量)
        for i, c in enumerate(seq):  # 遍历输入的有效括号字符串
            if c == '(':
                ans[i] = x & 1  # 根据当前深度的奇偶性分配给A或B
                x += 1  # 遇到左括号,深度增加
            else:
                x -= 1  # 遇到右括号,深度减少
                ans[i] = x & 1  # 与处理左括号相同的逻辑,但先减少深度
        return ans  # 返回分配方案

Explore

通过使每个括号的深度根据其奇偶性分配到不同的字符串,可以保证在任一给定字符串中,不会连续地增加嵌套深度。这样做的结果是,当一个字符串接收到一个深度为奇数的左括号时,另一个字符串则接收到一个深度为偶数的左括号,从而在两个字符串中交替增加深度。这种分配方式有效地将可能的最大深度平均分配给两个子字符串,避免了一个字符串独自承受过高的嵌套深度,从而实现了最大嵌套深度的最小化。

变量x通过在遇到左括号时增加1,遇到右括号时减少1,来跟踪未匹配的左括号数量。这种方法准确地反映了在任何给定点的嵌套深度。这种方法假设输入字符串是有效的括号字符串,即每个左括号都有一个对应的右括号。如果输入字符串不是有效的括号字符串,那么变量x可能无法准确反映实际的嵌套深度,因为它可能会出现负值(即右括号比左括号多)或最终不归零(即左括号比右括号多)。因此,如果输入包含非有效括号字符串,该方法的行为是未定义的,可能导致错误的输出。

有效括号字符串的长度总是偶数,因为每个左括号必须匹配一个右括号。因此,对于长度为奇数的输入字符串,它不是一个有效的括号字符串,所以此分配策略并不适用。如果不小心输入了一个长度为奇数的字符串,这将是一个错误的输入,因为它违反了有效括号字符串的定义。

如果输入字符串seq为空,代码将返回一个空列表。这是因为代码开始时初始化了一个长度与输入字符串相同的列表ans,如果输入为空,ans也将为空。这种情况下代码不需要特殊处理,因为它已经能够正确地处理空输入,返回一个空的结果列表。