-2

I tried to solve a challenge today and I couldn't. I had to make sure that all the braces of a string are closed and in the correct order.

For instance, these would be correct:

{[()]}
{}()[]
{((()))}[]

These would not:

{[}]
{{)}

I thought about doing with regex, I couldn't, can anyone show how to do it? (regex or other ways).

To clarify, I'm not trying to get the content inside the braces, there isn't any content inside.

BJ Myers
  • 6,617
  • 6
  • 34
  • 50
  • Show what you tried to resolve it – AlexGuerra May 17 '17 at 20:25
  • 2
    Good read - http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags (you should read all answers obviously to understand why in general parsing nested structures is not possible with regex, but to save you some time - http://stackoverflow.com/a/7564061/477420 gives you an option if you can use .Net's regexes) – Alexei Levenkov May 17 '17 at 20:26
  • 1
    Walk through it one character at a time - push open braces onto a stack, and for close braces, pop the top character off the stack and make sure they match. – BJ Myers May 17 '17 at 20:26
  • Which language are you trying to use a regex in? Engines differ in features/syntax. – melpomene May 17 '17 at 20:29
  • @Alex, It was a challenge in a timed environment, I didn't keep the code. – Gabriel Reis May 17 '17 at 22:49
  • @BJ Myers, but that wont "cover" for when I have a brace closing in the wrong place, for instance, {[ }] this is invalid because the closing ] is outside the { }. – Gabriel Reis May 17 '17 at 22:51
  • Proper search term: https://www.bing.com/search?q=regex+check+balanced+parentheses if you want more answers. – Alexei Levenkov May 17 '17 at 23:07
  • @GabrielReis Yes it will - that's the purpose of comparing the brace on the stack with the one in the string. I've posted an answer with the steps written out in more detail. – BJ Myers May 17 '17 at 23:13
  • @BJ Myers where did you posted? – Gabriel Reis May 17 '17 at 23:16
  • @GabrielReis As an answer to this question. (You might have to refresh the page?) – BJ Myers May 17 '17 at 23:23

2 Answers2

3

This can be solved in one pass through the string using a LIFO stack. Loop through the string, and at each character do the following:

  • If the current character is a "left" brace, add it to the stack.
  • If the current character is a "right" brace, pop the top character from the stack and compare.
    • If the "right" brace you just found is the same type as the "left" brace from the stack, those two braces are correctly matched. Keep going.
    • If the "right" brace you just found does not match the "left" brace from the stack, your braces are out of order. Return false.
    • If the stack is empty when you try to pop, you have too many trailing braces. Return false.

Once you make it to the end of the string, simply check the number of elements on the stack.

  • If there is anything on the stack, you have too many leading braces. Return false.
  • If the stack is empty, your braces are balanced. Return true.

Sample implementation in C# (sans error handling for non-brace characters):

public static bool IsMatched(string braces)
{
    const string openBraces = "([{";
    const string closeBraces = ")]}";

    Stack<char> stack = new Stack<char>();
    foreach (char c in braces)
    {
        if (openBraces.Contains(c))
        {
            stack.Push(c);
        }
        else if (stack.Count == 0 || openBraces.IndexOf(stack.Pop()) != closeBraces.IndexOf(c))
        {
            return false;
        }
    }
    return stack.Count == 0;
}
BJ Myers
  • 6,617
  • 6
  • 34
  • 50
1

The problem can be solved by regex, but it is not the fastest solution. Just remove all [], {} and () until the string doesn't shrink any more. If its length is 0 the expression was correct (the regex below additionally allows spaces):

document.body.innerHTML = parse('{((()))}[]');

function parse(s) {
    let t;
    do {
        t = s;
        s = s.replace(/ *(\{ *\}|\( *\)|\[ *\]) */g, "");
    } while (t.length != s.length);
    return s.length == 0;
}
maraca
  • 8,468
  • 3
  • 23
  • 45
  • Good on you for answering this. Quote a handy little function. – hmedia1 May 17 '17 at 22:16
  • But that wont "cover" for when I have a brace closing in the wrong place, for instance, {[ }] this is invalid because the closing ] is outside the { }. – Gabriel Reis May 17 '17 at 22:52
  • 1
    @GabrielReis it will work because only `{}` or `[]` is removed but not `{]}` or `[}]`, so this string will not be shortened by the regex and immediately returns false. – maraca May 17 '17 at 22:56
  • Regex here is mostly to confuse people : `s = s.replace("{}","").replace("()","").replace("[]","")` would be much easier to read/explain the approach... So not really regex solution - so instead could be duplicate. – Alexei Levenkov May 17 '17 at 23:05
  • @AlexeiLevenkov except that this solution would be even worse because it starts to look from the start for each replace and does only replace a single occurence each time. If you want to simplify: `s = s.replace(/\{\}|\[\]|\(\)/g, "");` – maraca May 17 '17 at 23:10
  • @maraca you are right. I run in js and it worked, but in C# it didn't. But thanks, it helped me understand how to do it. – Gabriel Reis May 17 '17 at 23:14
  • @GabrielReis I don't know C#, but it is very similar to Java and there Regex are defined differently (seperate classes or shortcuts via strings, no /.../ notation), also .length() is a function there and not a field. – maraca May 17 '17 at 23:18