# 「力扣」第 1111 题:有效括号的嵌套深度(中等)

# 题目描述

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

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

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

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

  • 不相交:每个 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. 连接,可以记作 AB(A 与 B 连接),其中 A 和 B 都是有效括号字符串
  3. 嵌套,可以记作 (A),其中 A 是有效括号字符串

嵌套深度:

类似地,我们可以定义任意有效括号字符串 s 的 嵌套深度 depth(S):
  1. s 为空时,depth("") = 0
  2. s 为 A 与 B 连接时,depth(A + B) = max(depth(A), depth(B)),其中 A 和 B 都是有效括号字符串
  3. s 为嵌套情况,depth("(" + A + ")") = 1 + depth(A),其中 A 是有效括号字符串

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

# 分析题意

1、首先需要弄清楚「有效括号」的意思。

  • 如果做过「力扣」第 20 题:有效的括号 (opens new window),就不难明白有效括号的含义:就像我们写代码一样,{} (本题是 ())需要成对出现,才叫匹配;
  • 由于匹配操作符合「后进先出」的特点:遇到进来的右括号 ) ,它匹配后进来的左括号 (,它相对于之前扫描到的左括号 ( 先完成匹配,这种匹配工作需要使用的数据结构是「栈」;
  • 在完成括号匹配的过程中,只需要把遍历到的左括号进栈,在遍历到右括号的时候,左括号出栈,这个思路是经典且重要的。

2、看题目:

连接,可以记作 ABAB 连接),其中 AB 都是有效括号字符串。

说明连在一起的时候,以「有效括号字符串」为最小单位。例如:A = ")("B = "()" 就不能连接在一起,因为 A 不是有效括号字符串。

3、看题目:

嵌套,可以记作 (A),其中 A 是有效括号字符串; sAB 连接时,depth(A + B) = max(depth(A), depth(B)),其中 AB 都是有效括号字符串; 例如:"""()()",和 "()(()())" 都是有效括号字符串,嵌套深度分别为 012,而 ")(""(()" 都不是有效括号字符串。

从这些信息中,可以知道:

  • 「嵌套」才会产生「深度」,所以题目的定义是嵌套深度,极端例子 1:()()()(),这个字符串的「嵌套深度」为 。极端例子 2:(((()))) 这个字符串的「嵌套深度」为 。而这个深度恰好与在匹配过程中,栈的最大高度是一致的;

  • 在「有效括号字符串」连接的时候,depth(A + B) = max(depth(A), depth(B)) 的意思是大吃小,即在两个「有效括号字符串」连接的时候,整体深度取决于部分「有效括号字符串」的深度,这一点特别像在二叉树中的计算二叉树的高度

  • 其实仔细观察就会发现,这个「嵌套深度」就是输入字符串,使用栈完成括号匹配,栈中最多连续出现的左括号 ( 的个数

  • 而题目要求我们把输入的整体有效字符串做一个重组,要求是只拆成两个部分 AB ,每个字符要么分到 A 要么分到 B,分到 A 标记为 0,分到 B 标记为 1。而每个部分的字符又要保持在输入字符串中的顺序不变。因此输出是一个与原始字符串等长的整数数组,这个整数数组里只有 01

例如示例 1:

输入:seq = "(()())",嵌套深度为 2,最多连续出现了 2 个左括号。现在要拆分以后再重组,其实思路就有了,把这两个连续出现的左括号分到不同组即可。

输出:[0, 1, 1, 1, 1, 0] 对应了输入字符串的每个字符对应的组号,0 分到 A 组,1 分到 B 组。

标记为 0 的组的「嵌套深度」是 1;标记为 1 的组的「嵌套深度」是 1,因此总的「嵌套深度」就是 1。

从这个示例中我们知道,重组以后的嵌套深度是原始嵌套深度的一半(不能整除的时候上取整)。

  • 如果原始嵌套深度是偶数,例如 6,重组的嵌套深度 = 3;
  • 如果原始嵌套深度是奇数,例如 7,重组的嵌套深度 = 4。

示例 2:

说明:结果不唯一,但一定要保证连续的左括号 (( 不被分在同一组,具体来说,就是完成「括号匹配」问题使用的栈里,同时出现的左括号,不能在同一个分组里,这样就不会增加嵌套的深度。

# 思路分析

  • 根据 depth(A + B) = max(depth(A), depth(B)) 这个定义,整体的「嵌套深度」取决于子序列的「嵌套深度」的最大者;
  • 要使得 max(depth(A), depth(B)) 的可能取值最小,分析示例的时候提到,这很像一棵二叉树,要使得二叉树的深度最小,那么就需要该二叉树平衡,一个可行的做法是:把栈中连续出现的左括号 ( 根据奇偶性分到不同的组,右括号随与之匹配左括号的组号
  • 如果出现 () 这种子序列,即左括号后面连着出现了右括号,其实分在那一组都是没有关系的,因为它的存在不会使得「嵌套深度」更深。

置顶的评论总结了解决这道题的核心思想,大家可以看一下。

参考代码

public class Solution {

    public int[] maxDepthAfterSplit(String seq) {

        int len = seq.length();
        int[] res = new int[len];

        // 嵌套深度,栈的当前高度
        int depth = 0;

        // 在 Java 里,seq.charAt(i) 函数会做下标越界检查,
        // 因此先转换成字符数组是常见的做法
        char[] charArray = seq.toCharArray();

        for (int i = 0; i < len; i++) {
            // 遍历到左括号,连续括号个数加 1,
            if (charArray[i] == '(') {
                depth++;
                // % 2 也可以写成 & 1,为了保证语义清楚,写 % 2
                res[i] = depth % 2;
            } else {
                // 遍历到右括号,与当前栈顶左括号分在一组,因此先取模,再 --
                // 这一步希望大家多体会,很有意思
                res[i] = depth % 2;
                depth--;
            }
        }
        return res;
    }
}

大家还可以在「力扣」上搜索一些「括号匹配」的问题来做,在题库里搜索关键字「括号」,相信是非常有意思的问题。

今天是 4 月 1 日,官方选择第 1111 号问题,是不是很有意思呢。今天在题库里搜 reverse 或者 mirror 的题,试试看。

另外这道题的标签是「贪心」和「二分」。

  • 「二分」应该是标错了,一分为二不能叫「二分」;
  • 「贪心」个人觉得用只看局部,得到整体最优,这样解释感觉有点牵强。

作者:liweiwei1419 链接:https://suanfa8.com/stack/solutions-1/1111-maximum-nesting-depth-of-two-valid-parentheses-strings 来源:算法吧 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Last Updated: 11/19/2024, 1:33:17 AM