如何在具有恒定内存的线性时间内在字符串中找到所有不平衡的括号?


11

面试中给了我以下问题:

给出一个字符串,其中包含一些括号(不是方括号或大括号,仅是括号)与其他字母数字字符的混合,标识所有没有匹配括号的括号。

例如,在字符串“)(ab))中,索引0和5包含没有匹配括号的括号。

我提出了一个使用O(n)内存的有效O(n)解决方案,使用了堆栈,并在将括号添加到堆栈中并遍历字符串后,每当遇到封闭括号和堆栈顶部时,将其从堆栈中删除开场菜。

之后,访问员指出,可以使用恒定的内存在线性时间内解决问题(例如,除了输入占用的内存之外,没有其他内存使用)。

我问了如何,她说了一些关于从左边一次识别所有打开的parens,然后从右边再次识别所有闭合的parns ....或者相反的说法。我不是很了解,也不想让她手牵着我。

谁能澄清她建议的解决方案?


1
我们可能首先需要您澄清一下。“(())”中的第一个或第二个括号是否被认为不平衡?“(())”中的最后一个或第二个括号是否被认为不平衡?还是足以确定基数最少的任何一组parens,以便将其删除将使其余parens保持平衡?或者是其他东西?还是在面试的这一部分,以便答案可以提出任何合理的说明?
John L.

我想这没关系,由您决定。删除任何使其余部分保持平衡的集。
临时用户名

5
然后将其全部删除; P
Veedrac

@Veedrac,当然(如您所知),发布者忘记了“删除任何最小集…… ”中的“最小”一词。
LSpice

我本身并没有“忘记”它,而是把它遗漏了,因为它对我来说似乎不是一个重要的规范,因为除了 “所有这些” 之外,只有一组可以删除以使其平衡。当然会破坏练习的目的。
临时用户名

Answers:


17

由于这来自编程背景,而不是理论上的计算机科学练习,因此我假设将索引存储到字符串中需要O(1)内存。在理论计算机科学中,这将意味着使用RAM模型。在Turing机器上,您无法执行此操作,因此需要Θ(log(n))内存才能将索引存储到长度为n的字符串中。

您可以保留所使用算法的基本原理。您错过了进行内存优化的机会。

使用堆栈并遍历字符串,一旦我将括号添加到堆栈中并将它们从堆栈中删除,每当我遇到一个封闭的括号并且堆栈顶部包含一个开头的括号时

那么这个堆栈包含什么呢?它永远不会包含()(左括号和右括号),因为每当)出现时,您弹出(而不是按即可)。因此,堆栈的形式始终是)…)(…(:一堆右括号,然后是一串开放括号。

您不需要堆栈来表示这一点。只需记住右括号的数量和右括号的数量即可。

如果使用这两个计数器从左到右处理字符串,那么最后您将得到不匹配的右括号数和不匹配的右括号数。

Θ(n)

总结:从左到右处理字符串。保持一个无与伦比的左括号。如果看到圆括号,请增加计数器。如果看到圆括号,并且计数器不为零,则递减计数器。如果看到右括号,并且计数器为零,则将当前索引输出为不匹配的右括号。

计数器的最终值是不匹配的左括号的数量,但这不会给您它们的位置。请注意,问题是对称的。要列出不匹配的左括号的位置,只需以相反的方向运行该算法即可。

练习1:以正式符号(数学,伪代码或您喜欢的编程语言)写下来。

练习2:使自己相信这与Apass.Jack的算法相同,只是解释不同。


哦,很好的吉尔,很好的解释了。我现在完全了解。自从我就您的一个问题得到您的答复以来已有好几年了。
临时用户名

“如果想在最后报告不匹配的括号的位置,则需要记住每个括号的位置。” 不完全的。线性时间并不意味着单次通过。您可以进行第二遍查找在不匹配的一侧找到任何括号并标记它们。
Mooing Duck,

对于最后一步,你不必在反向运行它,你可以简单地标记最后N“(”为不匹配。
鸣叫鸭

1
@MooingDuck那是行不通的。例如(()
orlp

虽然我真的很喜欢这个答案,但是有些事情困扰着我。那就是“我某种程度上需要记住位置,我认为我遇到的问题是:如何在不消耗内存的情况下(或在非常具体的上下文中以某种方式消耗您的输出来”输出当前索引”) –输出的顺序无关紧要。)
Édouard'19

8

由于我们只能忽略所有字母数字字符,因此从现在开始,我们将假定字符串仅包含括号。就像在问题中一样,只有一种括号“()”。

如果我们一直删除平衡括号,直到无法再删除平衡括号,则所有剩余的括号必须看起来像“))...((...(”,它们都是不平衡括号。此观察结果表明,我们应该首先找到该转折点,在此之前,我们仅具有不平衡的右括号,而在此之后,我们仅具有不平衡的右括号。

这是算法。简而言之,它首先计算转折点。然后输出额外的结束括号,从右到右扫描字符串直到转折点。对称地,它输出额外的左括号,从末端到左侧扫描直到转折点。


strn

初始化turning_point=0, maximum_count=0, count=0。对于每个ifrom 0n-1请执行以下操作。

  1. 如果str[i] = ')'加上1 count; 否则,减去1。
  2. 如果count > maximum_count,设置turning_point=imaximum_count=count

现在turning_point是转折点的索引。

复位maximum_count=0, count=0。对于每个ifrom 0turning_point请执行以下操作。

  1. 如果str[i] = ')'加上1 count; 否则,减去1。
  2. 如果count > maximum_count设置maximum_count = count。输出i为不平衡右括号的索引。

复位maximum_count=0, count=0。对于每个i从下n-1turning_point+1下的操作,请执行以下操作。

  1. 如果str[j] = '('加上1 count; 否则,减去1。
  2. 如果count > maximum_count设置maximum_count = count。输出i为不平衡的左括号的索引。

O(n)O(1)O(u)u


如果我们分析了上面的算法,我们将会发现,实际上,我们根本不需要查找和使用转折点。很好的观察是,所有不平衡的右括号都发生在所有不平衡的右括号之前,尽管这很有趣,但可以忽略不计。

这是Python中的代码

只需点击“运行”即可查看几个测试结果。


练习1.显示以上算法将输出一组基数最小的括号,以使其余括号保持平衡。

问题1.我们可以将算法推广到字符串包含“()[]”之类的两种括号的情况吗?我们必须确定如何识别和处理新情况,即交错情况“([])”。


大声笑,练习1和问题1,很可爱。您所描述的算法的逻辑令人惊讶地难以想象。我明天必须对此进行编码才能得到它。
临时用户名

看来我错过了相当明显但最重要的解释。逻辑实际上很简单。首先,我们输出每个额外的左括号。通过转折点后,我们将输出每个额外的右括号。做完了
John L.

查找不平衡的开括号是不正确的。即,如果您的arr为“())”,则p为2,p + 1落在arr边界之外。只是一个想法-要找到不平衡的开括号,您可以反转arr并使用算法的一部分来找到不平衡的开括号(当然,具有反向匹配的索引)。
OzrenTkalcecKrznaric

p+1

让我稍微了解了一下,但是我喜欢它,它非常聪明..并且至少对我想过的每种情况都有效
dquijada
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.