带平衡括号的子串
substrings with Balanced parentheses
我的朋友在一次采访中被问到以下问题:给定一个仅由“(”和“)”组成的字符串。找到带平衡括号的子串总数注意:输入字符串已经平衡。
我能想到这个问题的唯一解决方案是蛮力,这需要 n^3 次。有没有更快的解决方案 possible.If 那么我也想知道该方法的构建。
我做了一个简单的算法来解决你的问题。请注意,它不会查找嵌套的平衡括号。
function TestAlgorithm(testString, resultCounter)
{
if (!resultCounter)
resultCounter = 0;
var startIndex = testString.indexOf('(');
if (startIndex === -1)
return resultCounter;
var endIndex = testString.indexOf(')', startIndex)
if (endIndex === -1)
return resultCounter;
var newTestString = testString.substring(endIndex);
return TestAlgorithm(newTestString, ++resultCounter);
}
假设最终结果是一个整数R,你应该在字符串上从左到右扫描然后,你应该保留一个堆栈Z,并在从左到右扫描时更新它。
您应该首先将 0 推入 Z。当您在索引 i 处遇到 '(' 时,您应该将 0 推入 S。当您在索引 i 处遇到 ')' 时,您应该将 R 递增 (T * (T+1) / 2), T 是 Z 的顶部元素。然后你应该弹出 T,并将 新的顶部元素 递增 1.
一旦扫描完成,你应该将R再递增一次(T * (T+1) / 2),因为在Z中还有一个我们最初放入的元素T。
使用堆栈 Z 的扫描应该花费 线性时间。下面是一个效率不高的 Python 实现,希望易于理解。
def solve(s):
R = 0
Z = [0]
for i in range(0, len(s)):
if s[i] == '(':
Z.append(0)
else:
R += Z[-1] * (Z[-1] + 1) / 2
Z = Z[:-1]
Z[-1] += 1
R += Z[-1] * (Z[-1] + 1) / 2
return R
递增 R 背后的想法如下。基本上你保持 连续 同级平衡字符串的数量,直到即将离开该级别。然后,当你即将进入更高层次时(即当我们知道不会有任何其他同级连续子串时,我们更新解决方案。
T * (T + 1) / 2 的值,如果你从不同的角度考虑间隔,就可以理解。让我们枚举从 1 到 T 的那些连续的同级平衡子串。现在,使用这些选择平衡子串基本上是为我们的较大子串选择起点和终点。如果我们选择子串 #1 作为起点,那么还有 T 个其他子串可以作为终点。对于#2,有 (T-1),依此类推。基本上我们可以选择 T*(T+1)/2 个不同的间隔作为有效的平衡子串,这就是为什么我们将 R 递增该值。
我们对R最后的自增操作只是为了不遗漏最外层
范围内的每个子字符串都以“(”开头。所以我的非递归方法是:
total = 0
while string is not empty {
count valid substrings beginning here -- add to total
trim leading '(' and trailing ')' from string
trim leading ')' and trailing '(' from string if present
}
count valid substrings beginning here
可以通过一个字符一个字符地逐步执行,在看到“(”时递增计数器,在看到“)”时递减计数器来完成。当递减结果为零时,您位于平衡子串的结尾 ')'。
我的朋友在一次采访中被问到以下问题:给定一个仅由“(”和“)”组成的字符串。找到带平衡括号的子串总数注意:输入字符串已经平衡。
我能想到这个问题的唯一解决方案是蛮力,这需要 n^3 次。有没有更快的解决方案 possible.If 那么我也想知道该方法的构建。
我做了一个简单的算法来解决你的问题。请注意,它不会查找嵌套的平衡括号。
function TestAlgorithm(testString, resultCounter)
{
if (!resultCounter)
resultCounter = 0;
var startIndex = testString.indexOf('(');
if (startIndex === -1)
return resultCounter;
var endIndex = testString.indexOf(')', startIndex)
if (endIndex === -1)
return resultCounter;
var newTestString = testString.substring(endIndex);
return TestAlgorithm(newTestString, ++resultCounter);
}
假设最终结果是一个整数R,你应该在字符串上从左到右扫描然后,你应该保留一个堆栈Z,并在从左到右扫描时更新它。
您应该首先将 0 推入 Z。当您在索引 i 处遇到 '(' 时,您应该将 0 推入 S。当您在索引 i 处遇到 ')' 时,您应该将 R 递增 (T * (T+1) / 2), T 是 Z 的顶部元素。然后你应该弹出 T,并将 新的顶部元素 递增 1.
一旦扫描完成,你应该将R再递增一次(T * (T+1) / 2),因为在Z中还有一个我们最初放入的元素T。
使用堆栈 Z 的扫描应该花费 线性时间。下面是一个效率不高的 Python 实现,希望易于理解。
def solve(s):
R = 0
Z = [0]
for i in range(0, len(s)):
if s[i] == '(':
Z.append(0)
else:
R += Z[-1] * (Z[-1] + 1) / 2
Z = Z[:-1]
Z[-1] += 1
R += Z[-1] * (Z[-1] + 1) / 2
return R
递增 R 背后的想法如下。基本上你保持 连续 同级平衡字符串的数量,直到即将离开该级别。然后,当你即将进入更高层次时(即当我们知道不会有任何其他同级连续子串时,我们更新解决方案。
T * (T + 1) / 2 的值,如果你从不同的角度考虑间隔,就可以理解。让我们枚举从 1 到 T 的那些连续的同级平衡子串。现在,使用这些选择平衡子串基本上是为我们的较大子串选择起点和终点。如果我们选择子串 #1 作为起点,那么还有 T 个其他子串可以作为终点。对于#2,有 (T-1),依此类推。基本上我们可以选择 T*(T+1)/2 个不同的间隔作为有效的平衡子串,这就是为什么我们将 R 递增该值。
我们对R最后的自增操作只是为了不遗漏最外层
范围内的每个子字符串都以“(”开头。所以我的非递归方法是:
total = 0
while string is not empty {
count valid substrings beginning here -- add to total
trim leading '(' and trailing ')' from string
trim leading ')' and trailing '(' from string if present
}
count valid substrings beginning here
可以通过一个字符一个字符地逐步执行,在看到“(”时递增计数器,在看到“)”时递减计数器来完成。当递减结果为零时,您位于平衡子串的结尾 ')'。