Как найти правильность строки круглых скобок, фигурных скобок и квадратных скобок?
Недавно я связался с этой интересной проблемой. Вам предоставляется строка, содержащая только символы '('
, ')'
, '{'
, '}'
, '['
и ']'
, например, "[{()}]"
, вам нужно написать функцию, которая проверяет правильность такой входной строки, функция может быть такой:
bool isValid(char* s);
эти скобки должны быть закрыты в правильном порядке, например "()"
и "()[]{}"
являются действительными, но "(]"
, "([)]"
и "{{{{"
не являются!
Я вышел со следующим решением O (n) и O (n) пространственной сложности, которое отлично работает:
- Поддерживать стек символов.
- Всякий раз, когда вы обнаруживаете открывающиеся фигурные скобки
'('
, '{'
ИЛИ '['
, надавите на стек.
- Всякий раз, когда вы обнаруживаете закрывающие фигурные скобки
')'
, '}'
ИЛИ ']'
, проверьте, соответствует ли верхняя часть стека соответствующей открывающей скобке, если да, затем поместите стек, иначе сломайте цикл и верните false.
- Повторите шаги 2 - 3 до конца строки.
Это работает, но мы можем оптимизировать его для пространства, может быть постоянным дополнительным пространством, я понимаю, что временная сложность не может быть меньше O (n), поскольку мы должны смотреть на каждый символ.
Итак, мой вопрос: мы можем решить эту проблему в O (1) пространстве?
Ответы
Ответ 1
На самом деле существует детерминированный алгоритм лог-пространства из-за Ritchie и Springsteel: http://dx.doi.org/10.1016/S0019-9958(72)90205-7 ( paywalled, извините не в сети). Поскольку нам нужны лог-биты для индексации строки, это оптимально для пространства.
Если вы согласны принять одностороннюю ошибку, тогда существует алгоритм, в котором используется n полилог (n) время и полилог (n): http://www.eccc.uni-trier.de/report/2009/119/
Ответ 2
Что касается превосходного ответа от Matthieu M., вот реализация на С#, которая кажется прекрасно работающей.
/// <summary>
/// Checks to see if brackets are well formed.
/// Passes "Valid parentheses" challenge on www.codeeval.com,
/// which is a programming challenge site much like www.projecteuler.net.
/// </summary>
/// <param name="input">Input string, consisting of nothing but various types of brackets.</param>
/// <returns>True if brackets are well formed, false if not.</returns>
static bool IsWellFormedBrackets(string input)
{
string previous = "";
while (input.Length != previous.Length)
{
previous = input;
input = input
.Replace("()", String.Empty)
.Replace("[]", String.Empty)
.Replace("{}", String.Empty);
}
return (input.Length == 0);
}
По существу, все, что он делает, это удалить пары скобок, пока их не осталось удалить; если что-то осталось, скобки не сформированы.
Примеры хорошо сформированных скобок:
()[]
{()[]}
Пример неправильных скобок:
([)]
{()[}]
Ответ 3
Если вход доступен только для чтения, я не думаю, что мы можем сделать O (1) пространство. Хорошо известно, что любой разрешимый язык пространства O (1) является регулярным (записывается как регулярное выражение). Набор строк, который у вас есть, не является обычным языком.
Конечно, речь идет о машине Тьюринга. Я бы ожидал, что это будет верно и для аппаратов с фиксированной памятью.
Ответ 4
Изменить: Хотя это просто, этот алгоритм является фактически O (n ^ 2) в терминах сопоставлений символов. Чтобы продемонстрировать это, можно просто сгенерировать строку как '(' * n + ')' * n
.
У меня есть простая, хотя, возможно, ошибочная идея, что я буду подчиняться вашей критике.
Это разрушительный алгоритм, а это значит, что если вам понадобится строка, это не поможет (так как вам нужно будет ее скопировать).
В противном случае алгоритм работает с простым индексом в текущей строке.
Идея состоит в том, чтобы удалить пары один за другим:
-
([{}()])
-
([()])
-
([])
-
()
-
empty
→ OK
Он основан на простом факте, что если у нас есть соответствующие пары, то по крайней мере один имеет вид ()
без какого-либо пара-символа между ними.
Алгоритм:
-
i := 0
- Найдите подходящую пару из
i
. Если ни один не найден, строка недействительна. Если он найден, пусть i
- индекс первого символа.
- Удалите
[i:i+1]
из строки
- Если
i
находится в конце строки, а строка не пуста, это сбой.
- Если
[i-1:i]
- совпадающая пара, i := i-1
и обратно в 3.
- Остальное, назад к 1.
Алгоритм O(n)
по сложности, потому что:
- каждая итерация цикла удаляет 2 символа из строки
- шаг 2., который является линейным, естественно связан (
i
не может расти бесконечно)
И это O(1)
в пространстве, потому что требуется только индекс.
Конечно, если вы не можете позволить себе уничтожить строку, вам придется ее скопировать, а это O(n)
в пространстве, поэтому никакой реальной выгоды там нет!
Если, конечно, я не ошибаюсь где-то... и, возможно, кто-то мог бы использовать оригинальную идею (есть где-то пара), чтобы улучшить эффект.
Ответ 5
Я сомневаюсь, что вы найдете лучшее решение, так как даже если вы используете внутренние функции для регулярного выражения или подсчета вхождений, они по-прежнему имеют стоимость O (...). Я бы сказал, что ваше решение лучше всего:)
Чтобы оптимизировать пространство, вы можете сделать некоторую кодировку в вашей стеке, но я сомневаюсь, что она вам очень понравится, за исключением случаев, когда {{{{{{{{{{}}}}}}}}}}
.
Ответ 6
http://www.sureinterview.com/shwqst/112007
Естественно решить эту проблему со стеком.
Если используются только '(' и ')', стек не нужен. Нам просто нужно поддерживать счетчик для непревзойденного слева '('. Выражение действительно, если счетчик всегда неотрицателен во время матча и равен нулю в конце.
В общем случае, хотя стек по-прежнему необходим, глубина стека может быть уменьшена с помощью счетчика для непревзойденных фигурных скобок.
Ответ 7
Это рабочий код Java, в котором я отфильтровываю скобки из строкового выражения, а затем проверяю правильность формы, заменяя хорошо сформированные фигурные скобки на нули
Пример input = (a+{b+c}-[d-e])+[f]-[g]
FilterBrackets выведет = ({}[])[][]
Затем я проверю корректность.
Комментарии приветствуются.
public class ParanString {
public static void main(String[] args) {
String s = FilterBrackets("(a+{b+c}-[d-e])[][]");
while ((s.length()!=0) && (s.contains("[]")||s.contains("()")||s.contains("{}")))
{
//System.out.println(s.length());
//System.out.println(s);
s = s.replace("[]", "");
s = s.replace("()", "");
s = s.replace("{}", "");
}
if(s.length()==0)
{
System.out.println("Well Formed");
}
else
{
System.out.println("Not Well Formed");
}
}
public static String FilterBrackets(String str)
{
int len=str.length();
char arr[] = str.toCharArray();
String filter = "";
for (int i = 0; i < len; i++)
{
if ((arr[i]=='(') || (arr[i]==')') || (arr[i]=='[') || (arr[i]==']') || (arr[i]=='{') || (arr[i]=='}'))
{
filter=filter+arr[i];
}
}
return filter;
}
}
Ответ 8
Следующая модификация ответа Sbusidan - это комплекс времени O (n 2), но O ( log n) просто.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
char opposite(char bracket) {
switch(bracket) {
case '[':
return ']';
case '(':
return ')';
}
}
bool is_balanced(int length, char *s) {
int depth, target_depth, index;
char target_bracket;
if(length % 2 != 0) {
return false;
}
for(target_depth = length/2; target_depth > 0; target_depth--) {
depth=0;
for(index = 0; index < length; index++) {
switch(s[index]) {
case '(':
case '[':
depth++;
if(depth == target_depth) target_bracket = opposite(s[index]);
break;
case ')':
case ']':
if(depth == 0) return false;
if(depth == target_depth && s[index] != target_bracket) return false;
depth--;
break;
}
}
}
}
void main(char* argv[]) {
char input[] = "([)[(])]";
char *balanced = is_balanced(strlen(input), input) ? "balanced" : "imbalanced";
printf("%s is %s.\n", input, balanced);
}
Ответ 9
Если вы можете перезаписать входную строку (не разумную в случаях использования, которую я себе представляю, но что за черт...), вы можете сделать это в постоянном пространстве, хотя я считаю, что потребность в времени идет до O (n 2).
Вот так:
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
Суть этого заключается в том, чтобы использовать раннюю часть ввода как стек.
Ответ 10
Я знаю, что немного опаздываю на эту вечеринку; это также мой самый первый пост в StackOverflow.
Но когда я просмотрел ответы, я подумал, что смогу придумать лучшее решение.
Итак, мое решение состоит в том, чтобы использовать несколько указателей.
Он даже не должен использовать память RAM, так как для этого могут использоваться регистры.
Я не тестировал код; он написал это на лету.
Вам нужно будет исправить мои опечатки и отладить его, но я верю, что вы получите эту идею.
Использование памяти: в большинстве случаев регистрируется только центральный процессор.
Использование ЦП: это зависит, но примерно в два раза больше времени, затрачиваемого на чтение строки.
Изменяет память: Нет.
b: string b eginning, e: string e nd.
l: l позиция eft, r: r позиция.
c: c har, m: m atch char
если r достигает конца строки, мы имеем успех.
l идет назад от r к b.
Всякий раз, когда r встречает новый тип начала, установите l = r.
когда l достигает b, мы закончили с блоком; перейти к началу следующего блока.
const char *chk(const char *b, int len) /* option 2: remove int len */
{
char c, m;
const char *l, *r;
e = &b[len]; /* option 2: remove. */
l = b;
r = b;
while(r < e) /* option 2: change to while(1) */
{
c = *r++;
/* option 2: if(0 == c) break; */
if('(' == c || '{' == c || '[' == c)
{
l = r;
}
else if(')' == c || ']' == c || '}' == c)
{
/* find 'previous' starting brace */
m = 0;
while(l > b && '(' != m && '[' != m && '{' != m)
{
m = *--l;
}
/* now check if we have the correct one: */
if(((m & 1) + 1 + m) != c) /* cryptic: convert starting kind to ending kind and match with c */
{
return(r - 1); /* point to error */
}
if(l <= b) /* did we reach the beginning of this block ? */
{
b = r; /* set new beginning to 'head' */
l = b; /* obsolete: make left is in range. */
}
}
}
m = 0;
while(l > b && '(' != m && '[' != m && '{' != m)
{
m = *--l;
}
return(m ? l : NULL); /* NULL-pointer for OK */
}
Подумав об этом подходе некоторое время, я понял, что он не будет работать так, как сейчас.
Проблема будет в том, что если у вас есть "[()()]", это не удастся при достижении "]".
Но вместо того, чтобы удалить предлагаемое решение, я оставлю его здесь, так как на самом деле не невозможно заставить его работать, но он требует некоторой модификации.
Ответ 11
/**
*
* @author madhusudan
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
new Main().validateBraces("()()()()(((((())))))()()()()()()()()");
// TODO code application logic here
}
/**
* @Use this method to validate braces
*/
public void validateBraces(String teststr)
{
StringBuffer teststr1=new StringBuffer(teststr);
int ind=-1;
for(int i=0;i<teststr1.length();)
{
if(teststr1.length()<1)
break;
char ch=teststr1.charAt(0);
if(isClose(ch))
break;
else if(isOpen(ch))
{
ind=teststr1.indexOf(")", i);
if(ind==-1)
break;
teststr1=teststr1.deleteCharAt(ind).deleteCharAt(i);
}
else if(isClose(ch))
{
teststr1=deleteOpenBraces(teststr1,0,i);
}
}
if(teststr1.length()>0)
{
System.out.println("Invalid");
}else
{
System.out.println("Valid");
}
}
public boolean isOpen(char ch)
{
if("(".equals(Character.toString(ch)))
{
return true;
}else
return false;
}
public boolean isClose(char ch)
{
if(")".equals(Character.toString(ch)))
{
return true;
}else
return false;
}
public StringBuffer deleteOpenBraces(StringBuffer str,int start,int end)
{
char ar[]=str.toString().toCharArray();
for(int i=start;i<end;i++)
{
if("(".equals(ar[i]))
str=str.deleteCharAt(i).deleteCharAt(end);
break;
}
return str;
}
}
Ответ 12
Вместо того, чтобы вставлять фигурные скобки в стек, вы можете использовать два указателя для проверки символов строки. один начинается с начала строки, а другой начинается с конца строки. что-то вроде
bool isValid(char* s) {
start = find_first_brace(s);
end = find_last_brace(s);
while (start <= end) {
if (!IsPair(start,end)) return false;
// move the pointer forward until reach a brace
start = find_next_brace(start);
// move the pointer backward until reach a brace
end = find_prev_brace(end);
}
return true;
}
Обратите внимание, что некоторые угловые случаи не обрабатываются.
Ответ 13
Я думаю, что вы можете реализовать алгоритм O (n). Просто вы должны инициализировать переменную счетчика для каждого типа: фигурные, квадратные и обычные скобки. После этого вы должны итерации строки и должны увеличить счетчик, если скобка открыта, в противном случае, чтобы уменьшить ее. Если счетчик отрицательный, верните false. AfterI думаю, что вы можете реализовать алгоритм O (n). Просто вы должны инициализировать переменную счетчика для каждого типа: фигурные, квадратные и обычные скобки. После этого вы должны итерации строки и должны увеличить счетчик, если скобка открыта, в противном случае, чтобы уменьшить ее. Если счетчик отрицательный, верните false. После того, как вы подсчитаете все скобки, вы должны проверить, равны ли все счетчики. В этом случае строка верна и вы должны вернуть true.
Ответ 14
Это мое решение проблемы.
O (n) - сложность времени без сложности пространства.
Код в C.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool checkBraket(char *s)
{
int curly = 0, rounded = 0, squre = 0;
int i = 0;
char ch = s[0];
while (ch != '\0')
{
if (ch == '{') curly++;
if (ch == '}') {
if (curly == 0) {
return false;
} else {
curly--; }
}
if (ch == '[') squre++;
if (ch == ']') {
if (squre == 0) {
return false;
} else {
squre--;
}
}
if (ch == '(') rounded++;
if (ch == ')') {
if (rounded == 0) {
return false;
} else {
rounded--;
}
}
i++;
ch = s[i];
}
if (curly == 0 && rounded == 0 && squre == 0){
return true;
}
else {
return false;
}
}
void main()
{
char mystring[] = "{{{{{[(())}}]}}}";
int answer = checkBraket(mystring);
printf("my answer is %d\n", answer);
return;
}
Ответ 15
Вы можете указать значение и проверить, является ли его действительным, оно будет печатать ДА, иначе оно будет печатать NO
static void Main(string[] args)
{
string value = "(((([{[(}]}]))))";
List<string> jj = new List<string>();
if (!(value.Length % 2 == 0))
{
Console.WriteLine("NO");
}
else
{
bool isValid = true;
List<string> items = new List<string>();
for (int i = 0; i < value.Length; i++)
{
string item = value.Substring(i, 1);
if (item == "(" || item == "{" || item == "[")
{
items.Add(item);
}
else
{
string openItem = items[items.Count - 1];
if (((item == ")" && openItem == "(")) || (item == "}" && openItem == "{") || (item == "]" && openItem == "["))
{
items.RemoveAt(items.Count - 1);
}
else
{
isValid = false;
break;
}
}
}
if (isValid)
{
Console.WriteLine("Yes");
}
else
{
Console.WriteLine("NO");
}
}
Console.ReadKey();
}
Ответ 16
var verify = function(text)
{
var symbolsArray = ['[]', '()', '<>'];
var symbolReg = function(n)
{
var reg = [];
for (var i = 0; i < symbolsArray.length; i++) {
reg.push('\\' + symbolsArray[i][n]);
}
return new RegExp('(' + reg.join('|') + ')','g');
};
// openReg matches '(', '[' and '<' and return true or false
var openReg = symbolReg(0);
// closeReg matches ')', ']' and '>' and return true or false
var closeReg = symbolReg(1);
// nestTest matches openSymbol+anyChar+closeSymbol
// and returns an obj with the match str and it start index
var nestTest = function(symbols, text)
{
var open = symbols[0]
, close = symbols[1]
, reg = new RegExp('(\\' + open + ')([\\s\\S])*(\\' + close + ')','g')
, test = reg.exec(text);
if (test) return {
start: test.index,
str: test[0]
};
else return false;
};
var recursiveCheck = function(text)
{
var i, nestTests = [], test, symbols;
// nestTest with each symbol
for (i = 0; i < symbolsArray.length; i++)
{
symbols = symbolsArray[i];
test = nestTest(symbols, text);
if (test) nestTests.push(test);
}
// sort tests by start index
nestTests.sort(function(a, b)
{
return a.start - b.start;
});
if (nestTests.length)
{
// build nest data: calculate match end index
for (i = 0; i < nestTests.length; i++)
{
test = nestTests[i];
var end = test.start + ( (test.str) ? test.str.length : 0 );
nestTests[i].end = end;
var last = (nestTests[i + 1]) ? nestTests[i + 1].index : text.length;
nestTests[i].pos = text.substring(end, last);
}
for (i = 0; i < nestTests.length; i++)
{
test = nestTests[i];
// recursive checks what after the nest
if (test.pos.length && !recursiveCheck(test.pos)) return false;
// recursive checks what in the nest
if (test.str.length) {
test.str = test.str.substring(1, test.str.length - 1);
return recursiveCheck(test.str);
} else return true;
}
} else {
// if no nests then check for orphan symbols
var closeTest = closeReg.test(text);
var openTest = openReg.test(text);
return !(closeTest || openTest);
}
};
return recursiveCheck(text);
};
Ответ 17
Использование программирования С# OOPS... Малое и простое решение
Console.WriteLine("Enter the string");
string str = Console.ReadLine();
int length = str.Length;
if (length % 2 == 0)
{
while (length > 0 && str.Length > 0)
{
for (int i = 0; i < str.Length; i++)
{
if (i + 1 < str.Length)
{
switch (str[i])
{
case '{':
if (str[i + 1] == '}')
str = str.Remove(i, 2);
break;
case '(':
if (str[i + 1] == ')')
str = str.Remove(i, 2);
break;
case '[':
if (str[i + 1] == ']')
str = str.Remove(i, 2);
break;
}
}
}
length--;
}
if(str.Length > 0)
Console.WriteLine("Invalid input");
else
Console.WriteLine("Valid input");
}
else
Console.WriteLine("Invalid input");
Console.ReadKey();