Я недавно вступил в контакт с этой интересной проблемой. Вам дают строку, содержащую просто символы '('
, ')'
, '{'
, '}'
, '['
и ']'
, например, "[{()}]"
, необходимо записать функцию, которая проверит законность такой входной строки, функция может быть похожей на это:
bool isValid(char* s);
эти скобки должны закрыться в правильном порядке, например "()"
и "()[]{}"
все допустимы, но "(]"
, "([)]"
и "{{{{"
не!
Я выпустил после O (n) время и O (n) решение для сложности пространства, которое хорошо работает:
'('
, '{'
ИЛИ '['
продвиньте его на стеке.')'
, '}'
ИЛИ ']'
, проверьте, является ли вершина стека соответствующей открывающей скобкой, если да, то вытолкайте стек, еще повредите цикл и возвратите false.Это работает, но можем мы оптимизировать его для пространства, может быть постоянное дополнительное пространство, я понимаю, что временная сложность не может быть меньше, чем O (n), поскольку мы должны посмотреть на каждый символ.
Таким образом, моим вопросом является банка, мы решаем эту проблему в O (1) пространство?
На самом деле, существует детерминированный алгоритм лог-пространства, созданный Ричи и Спрингстил: http://dx.doi.org/10.1016/S0019-9958 (72) 90205-7 ( платный , извините не в сети). Поскольку нам нужны биты журнала для индексации строки, это оптимально по пространству.
Если вы готовы принять одностороннюю ошибку, тогда есть алгоритм, который использует n полилог (n) времени и полилог (n) пространство: http://www.eccc.uni-trier.de / report / 2009/119 /
Редактировать: Несмотря на простоту, этот алгоритм фактически O (n ^ 2) с точки зрения сравнения символов. Чтобы продемонстрировать это, можно просто сгенерировать строку как '(' * n + ')' * n
.
У меня есть простая, хотя, возможно, ошибочная идея, что я подчинюсь вашей критике.
Это деструктивный алгоритм, а это значит, что если вам когда-нибудь понадобится строка, она не поможет (так как вам нужно будет скопировать ее).
В противном случае алгоритм будет работать с простым индексом в текущей строке.
Идея состоит в том, чтобы удалять пары одну за другой:
([{} ()])
([()])
([])
()
пусто
-> ОК
Это основано на том простом факте, что если у нас есть совпадающие пары, то по крайней мере одна имеет форму ()
без каких-либо парных символов между ними.
Алгоритм:
i: = 0
i
. Если ничего не найдено, значит строка недействительна. Если он найден, пусть i
будет индексом первого символа. [i: i + 1]
из строки i
находится в конце строки, а строка не пуста, это ошибка. [i-1: i]
- совпадающая пара, i: = i-1
и обратно к 3. Алгоритм составляет O (n)
по сложности, потому что:
i
не может расти бесконечно) И это O (1)
в пространстве, потому что требуется только индекс.
Конечно, если вы не можете позволить себе уничтожить строку, вам придется скопировать ее, и это O (n)
в космосе, так что никакой реальной пользы от этого нет!
Если, конечно, я где-то глубоко не ошибаюсь ... и, возможно, кто-то может использовать исходную идею (где-то есть пара) для лучшего эффекта.
Если входные данные доступны только для чтения, я не думаю, что мы можем сделать O(1) пространство. Хорошо известно, что любой решаемый язык с O(1) пространством является регулярным (т.е. записываемым как регулярное выражение). Набор строк, который у вас есть, не является регулярным языком.
Конечно, речь идет о машине Тьюринга. Я бы ожидал, что это будет верно и для машин с фиксированным количеством слов в оперативной памяти.
Я сомневаюсь, что вы найдете лучшее решение, так как даже если вы используете внутренние функции для регулярного регистрации или подсчета вхождений, они все равно имеют стоимость O(...). Я бы сказал, что ваше решение является лучшим :)
Для оптимизации пространства вы можете сделать некоторое кодирование длины выполнения в своем стеке, но я сомневаюсь, что это очень принесет вам пользу, за исключением таких случаев, как {}}}
.
Если вы можете перезаписать входную строку (не разумно в тех случаях использования, которые я предполагаю, но что за черт...), вы можете сделать это в постоянном пространстве, хотя я полагаю, что требование времени возрастает до O(n2).
Вот так:
string s = input
char c = null
int i=0
do
if s[i] isAOpenChar()
c = s[i]
else if
c = isACloseChar()
if closeMatchesOpen(s[i],c)
erase s[i]
while s[--i] != c ;
erase s[i]
c == null
i = 0; // Not optimal! It would be better to back up until you find an opening character
else
return fail
end if
while (s[++i] != EOS)
if c==null
return pass
else
return fail
Суть этого в том, чтобы использовать раннюю часть ввода в качестве стека.