首先,定义的“平衡”括号中的字符串作为一个字符串,例如,对于每一个“(”有一个独特的,相应的“)”后的地方,“(”。
例如,下面的字符串都“平衡”:
()
()()
(())()
而这些都不是:
)(
())(
鉴于括号的一个字符串(长度<= 1,000,000)和范围的查询的列表,找到平衡括号内的每个范围的最大长度为每个<= 100000个的查询(使用0索引的范围)
例如:
()))()(())
范围:[0,3] - >最长= 2: “()”
范围:[0,4] - >最长= 2: “()”
范围:[5,9] - >最长= 4: “(())”
我的想法如下:
首先,仅仅确定一个字符串是否是“平衡”,可以通过保持一个堆栈来完成。 如果你遇到一个“(”,推入堆栈,当你遇到一个“)”,弹出堆栈。 如果在年底的任何'(的遗体,然后字符串不是“平衡”。
然而,重复此为所有范围是O(N * M)的复杂性,其是用于输入的尺寸过长。
现在,当注意到范围查询,前缀资金和二进制索引树/段树浮现在脑海中。 如果你能预先计算整个前缀和范围到一个数组,然后你可以找到通过采取差别,这将具有良好的大O的复杂性更小的前缀款项。
我有一遇到一次分配一个+1值的“(”和一个值-1到“)”,所以这样的想法“(”你添加一个累计总和,当你遇到一个“) “你减少。 因此,对于一个有效的, “平衡”之类的字符串))()
你会得到: -1 -2 -1 -2
。
不过,我的问题是你怎么用它来确定它是否是“平衡”? 此外,因为你需要找到一个给定的时间间隔内最大的“平衡”的字符串,你怎么用的能力,以检查是否给定的子串是“平衡”,以找到这个最大的是给定的区间内。
介绍
对于位置的每个支架开始x
,你想找到的位置匹配的右括号y
,使得从子x
到y
, S[x, y]
是平衡的最大子。 我们不感兴趣,开始右括号子,因为开始的字符串最多有一样好,从第一个随后打开支架字符串。
最重要的观察是:用于开始在某个位置每打开托架x'
为x < x' < y
则匹配的结束托架是在y'
其中x' < y' < y
这是因为每一个前缀S[x, y]
含有至少一样多的开口括号作为闭括号,所以S[x', y]
至多含有足够多的开口作为闭括号。
我们可以利用这些知识来构建一个树,其中每个节点代表了最大的平衡子,所以它有一个起始位置和结束位置。 该节点的孩子们代表这个顶级子的平衡子。 由于有可能关闭不匹配的左括号括号,没有一个统一的根,所以我们实际上有一个森林1。
一张图片胜过一千多字。 考虑下面这个例子:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
) ) ( ( ) ( ( ) ) ) ) ( ( ) )
这将使下面的树:
(2, 9) (11, 14)
/ \ |
(3, 4) (5, 8) (12, 13)
|
(6, 7)
构建树
构建树是很容易的。 先建立一个空栈。 每当你遇到的位置的开口的支架x
:
- 创建节点
(x, ..)
- 添加节点作为当前在堆栈的顶部的节点的子节点(如果它存在,否则这是一个根)
- 推新节点在堆栈上
每当你遇到在位置右括号y
:
- 弹出节点
(x, ..)
即在堆栈的顶部(如果不存在这样的节点,这是一个不匹配的闭合支架:忽略) - 设置收盘指数:
(x, y)
您扫描串一次,并在每个步骤执行操作的常数,所以建筑结构的线性时间完成。
查询树
现在你需要运行查询。 给定一个查询(p, q)
你需要找到最大的节点(其中规模y - x + 1
),它完全包含在(p, q)
简单地做了两个二进制搜索找到你的树中的起点和终点。 (如果在位置的字符p
是右括号你寻找最小x > p
。同样,如果在位置的字符q
是你寻找最大的开口托架y < p
找到沿路径的最大间隔x
和y
。
如果你的树是很好的平衡,每个查询需要O(lg n)
时间。 最糟糕的情况下,该字符串与所有开放括号开始,所有的右括号结束。 那么查询可能需要线性时间。
1 我们可以通过,因为有无与伦比的右括号添加尽可能多的开括号字符串的前解决这个问题。