Длина самой длинной цепочки сбалансированных скобок в заданных диапазонах
Во-первых, определите строку "сбалансированных" скобок как строку, такую, что для каждого "(" есть один уникальный, соответствующий ")" где-то после этого "(".
Например, следующие строки все "сбалансированы":
()
() ()
(()) ()
пока это не так:
) (
()) (
Учитывая строку скобок (длина <= 1000 000) и список запросов диапазона, найдите максимальную длину сбалансированных скобок в каждом из диапазонов для каждого из <= 100 000 запросов (используя 0-индексирование для диапазонов)
Пример:
())) () (())
Диапазон: [0,3] -> Самый длинный = 2: "()"
Диапазон: [0, 4] -> Longest = 2: "()"
Диапазон: [5, 9] -> самый длинный = 4: "(())"
Мои мысли таковы:
Во-первых, просто определить, является ли строка "сбалансированной", можно, поддерживая стек. Если вы встретите "(", вставьте в стек, а когда встретите ")", выскользните из стека. Если в конце остается какой-либо '(', то строка не "сбалансирована".
Однако повторение этого для всех диапазонов является O(N*M) сложностью, которая слишком велика для размера входов.
Теперь, заметив запросы диапазона, приходят на ум префиксные суммы и деревья / сегментированные деревья с двоичными индексами. Если вы можете предварительно вычислить весь диапазон сумм префиксов в массиве, то вы можете найти меньшие суммы префиксов, взяв разницу, которая будет иметь большую сложность.
У меня была идея присвоить значение +1 для '(' и значение -1 для ')' таким образом, чтобы каждый раз, когда вы сталкиваетесь с '(' вы добавляете единицу к совокупной сумме и когда вы сталкиваетесь с ') "Вы уменьшаете. Таким образом, для правильной, "сбалансированной" строки, как ))()
вы получите: -1 -2 -1 -2
,
Тем не менее, мой вопрос, как вы используете это, чтобы определить, является ли он "сбалансированным"? Кроме того, поскольку вам нужно найти наибольшую "сбалансированную" строку в заданном интервале, как вы используете возможность проверить, является ли данная подстрока "сбалансированной", чтобы найти это наибольшее значение в этом заданном интервале.
1 ответ
Вступление
Для каждой стартовой скобки в положении 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Мы могли бы исправить это, добавив столько открывающих скобок к передней части строки, сколько есть непревзойденных закрывающих скобок.