首先,定义一个"平衡"的字符串。圆括号作为字符串 对于每一个'('有一个独特的,匹配的')'在那之后的某个地方 '('
例如,以下字符串均为" balance":
()
()()
(())()
虽然不是:
)(
())(
给定一串括号(长度< = 1,000,000)和范围查询列表,找到 每个范围内的平衡括号的最大长度 每个< = 100,000个查询(使用范围的0索引)
例如:
()))()(())
范围:[0,3] - >最长= 2:"()"
范围:[0,4] - >最长= 2:"()"
范围:[5,9] - >最长= 4:"(())"
我的想法如下:
首先,只需确定一个字符串是否是平衡的"可以通过维护堆栈来完成。如果你遇到一个'(&#39 ;,推入堆栈,当你遇到一个')'时,弹出堆栈。如果最后任何'('仍然存在,则该字符串不是"平衡。"
然而,对所有范围重复此操作是O(N * M)复杂度,这对于输入的大小来说太长了。
现在,在注意到范围查询时,会想到前缀sum和二进制索引树/段树。如果您可以将整个前缀和范围预先计算到一个数组中,那么您可以通过获取差异来找到较小的前缀和,这将具有很大的复杂性。
我有一个想法是为一个'('和一个-1值赋予')'所以每当你遇到一个'('你在累积金额中加一个,当你遇到一个')时你减少了所以对于一个有效的,平衡的"像))()
这样的字符串:-1 -2 -1 -2
。
但是,我的问题是你如何使用它来确定它是否平衡"?此外,因为你需要找到最大的"平衡"在给定间隔内的字符串,你如何使用检查给定子字符串是否平衡的能力"在给定的时间间隔内找到最大值。
答案 0 :(得分:1)
<强>简介强>
对于位置x
的每个起始括号,您希望在y
位置找到匹配的右括号,以便从x
到y
,{{ 1}},是平衡的最大子字符串。我们对从结束括号开始的子字符串不感兴趣,因为从那里开始的字符串最多与从第一个后续开始括号开始的字符串一样好。
最重要的观察结果如下:对于从S[x, y]
的某个位置x'
开始的每个左括号,匹配的右括号位于x < x' < y
y'
。这是因为x' < y' < y
的每个前缀至少包含与右括号一样多的左括号,因此S[x, y]
最多只包含 与开括号一样多。
我们可以使用这些知识来构建树,其中每个节点代表最大平衡子字符串,因此它具有起始位置和结束位置。此节点的子节点表示此顶级子字符串的平衡子字符串。由于可能存在与开括号不匹配的右括号,因此没有单个根,因此我们实际上有一个林 1 。
一张图片说的千言万语。考虑这个例子:
S[x', y]
这将给出以下树:
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
的字符是左括号,则查找最大的{{1}找到q
和y < p
路径的最大间隔。
如果您的树很平衡,每个查询都需要x
次。最坏的情况是,字符串从所有开口括号开始,以所有结束括号结束。然后查询可能需要线性时间。
1 我们可以通过在字符串前面添加尽可能多的左括号来解决这个问题,因为有不匹配的右括号。