49

I recently came in contact with this interesting problem. You are given a string containing just the characters '(', ')', '{', '}', '[' and ']', for example, "[{()}]", you need to write a function which will check validity of such an input string, function may be like this:
bool isValid(char* s);
these brackets have to close in the correct order, for example "()" and "()[]{}" are all valid but "(]", "([)]" and "{{{{" are not!

I came out with following O(n) time and O(n) space complexity solution, which works fine:

  1. Maintain a stack of characters.
  2. Whenever you find opening braces '(', '{' OR '[' push it on the stack.
  3. Whenever you find closing braces ')', '}' OR ']' , check if top of stack is corresponding opening bracket, if yes, then pop the stack, else break the loop and return false.
  4. Repeat steps 2 - 3 until end of the string.

This works, but can we optimize it for space, may be constant extra space, I understand that time complexity cannot be less than O(n) as we have to look at every character.

So my question is can we solve this problem in O(1) space?

Rajendra Uppal
  • 19,218
  • 15
  • 59
  • 57
  • 17
    I’m glad you didn’t ask for a regular expression … – Gumbo Mar 24 '10 at 16:19
  • Seems like you are missing a step in your algorithm. You pop the closing brace, but do not unpush the opening brace. – Jeff B Mar 24 '10 at 16:21
  • 1
    @Jeff B: The closing braces aren’t even pushed on a stack. Only the top opening brace is popped off the stack if the found closing brace corresponds to the top opening brace on the stack. – Gumbo Mar 24 '10 at 16:23
  • 1
    I don't think I am missing anything there, I have checked it and it runs fine. I never said I pop closing brace, see step 3, I said: closing brace is next token and I check if it matches with current top of the stack, then I pop stack, means take out opening brace and throw it, and I continue... – Rajendra Uppal Mar 24 '10 at 16:25
  • 1
    Shouln't you check if the stack is empty or not, when you reach the end of a string? – Konstantin Milyutin Nov 16 '15 at 17:23
  • Why "([)]" is unbalanced? – shinzou Feb 25 '18 at 14:02

17 Answers17

13

With reference to the excellent answer from Matthieu M., here is an implementation in C# that seems to work beautifully.

/// <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);
}

Essentially, all it does is remove pairs of brackets until there are none left to remove; if there is anything left the brackets are not well formed.

Examples of well formed brackets:

()[]
{()[]}

Example of malformed brackets:

([)]
{()[}]
Contango
  • 76,540
  • 58
  • 260
  • 305
11

Actually, there's a deterministic log-space algorithm due to Ritchie and Springsteel: http://dx.doi.org/10.1016/S0019-9958(72)90205-7 (paywalled, sorry not online). Since we need log bits to index the string, this is space-optimal.


If you're willing to accept one-sided error, then there's an algorithm that uses n polylog(n) time and polylog(n) space: http://www.eccc.uni-trier.de/report/2009/119/

user287792
  • 1,581
  • 9
  • 12
  • 7
    Can you outline the algorithm? – Svante Mar 24 '10 at 17:27
  • It looks as though that would require a trip to the library. I suspect that the algorithm in the second link can be derandomized and have its storage reduced to O(log n) bits by making many passes, but I haven't worked out the details. – user287792 Mar 24 '10 at 17:42
  • The first linked paper now appears to be available (click the PDF link in the top left of the page. On the page numbered 319, in Definition 3 we see "Dyck languages can be thought of as consisting of well-formed strings of matching labeled parentheses", which is what we're talking about here. But personally I'd need to spend a fair deal of time getting from this paper to code... – AakashM Feb 14 '13 at 10:41
  • @user287792 can't access the article but I came up with a (I believe similar) solution. But suppose the string is n bit long, and we would like to check an open bracket at position i correspond to a closed bracket at position n - i (or n - i +1 depending on how you index), what is the complexity of the operation "n-i"? Doesn't it require n space? Or could we have simply store something like {n,i,n} and decrement the second n in place? So we use O(log n) space? – xiaolingxiao Apr 13 '15 at 00:01
6

If the input is read-only, I don't think we can do O(1) space. It is a well known fact that any O(1) space decidable language is regular (i.e writeable as a regular expression). The set of strings you have is not a regular language.

Of course, this is about a Turing Machine. I would expect it to be true for fixed word RAM machines too.

  • 1
    It's trivially true for fixed-word RAM machines because O(1) bits suffices to address only a constant prefix of the input. Usually when people say O(1) space they actually mean O(1) words unless they're talking about a computational model that can stream the input. – user287792 Mar 24 '10 at 17:20
  • @algorithmist: Yes, without specifying the model, talking about complexities does not make too much sense. My main point was that even if O(n^2) time (or any other arbitrary time) is allowed, it is likely still not possible to do in O(1) space on TMs. RAM is a different model, and there are some assumptions which need to be made clear before talking about complexities, like you said, if we assume we only do indexing (instead of +1 or -1 along the input), then we need O(logn) space. –  Mar 24 '10 at 17:35
3

Edit: Although simple, this algorithm is actually O(n^2) in terms of character comparisons. To demonstrate it, one can simply generate a string as '(' * n + ')' * n.

I have a simple, though perhaps erroneous idea, that I will submit to your criticisms.

It's a destructive algorithm, which means that if you ever need the string it would not help (since you would need to copy it down).

Otherwise, the algorithm work with a simple index within the current string.

The idea is to remove pairs one after the others:

  1. ([{}()])
  2. ([()])
  3. ([])
  4. ()
  5. empty -> OK

It is based on the simple fact that if we have matching pairs, then at least one is of the form () without any pair character in between.

Algorithm:

  1. i := 0
  2. Find a matching pair from i. If none is found, then the string is not valid. If one is found, let i be the index of the first character.
  3. Remove [i:i+1] from the string
  4. If i is at the end of the string, and the string is not empty, it's a failure.
  5. If [i-1:i] is a matching pair, i := i-1 and back to 3.
  6. Else, back to 1.

The algorithm is O(n) in complexity because:

  • each iteration of the loop removes 2 characters from the string
  • the step 2., which is linear, is naturally bound (i cannot grow indefinitely)

And it's O(1) in space because only the index is required.

Of course, if you can't afford to destroy the string, then you'll have to copy it, and that's O(n) in space so no real benefit there!

Unless, of course, I am deeply mistaken somewhere... and perhaps someone could use the original idea (there is a pair somewhere) to better effect.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 5
    n + (n-2) + (n-4) + … is O(n^2). – kennytm Mar 25 '10 at 08:58
  • This wont work assuming the input string is something like this {[(])} – raym0nd Aug 09 '12 at 02:11
  • @raym0nd: The string your propose is not correct, and I think the algorithm I outline will detect it is not... what is your worry ? – Matthieu M. Aug 10 '12 at 10:06
  • The algorithm, though simple and nice, is not O(n), but O(n^2) – Konstantin Milyutin Nov 16 '15 at 17:42
  • This algorithm uses log(n) space to store the index. – Bjartur Thorlacius Mar 12 '17 at 19:55
  • @BjarturThorlacius: While technically correct, a single 64-bits integer is sufficient for any string of characters which can fit in the sum of all memory and disks ever made by mankind. So... – Matthieu M. Mar 12 '17 at 20:02
  • 2. doesn't check if the string is empty, an empty string can be defined as legal. Also, how do you "remove" from a string since it's immutable? You create a new string every time, so it's not O(1). – shinzou Feb 25 '18 at 13:42
  • @shinzou: Only particular implementations of a string are immutable, there are implementations where it is perfectly mutable. It does raise the question that removing from a string is not O(1) in terms of CPU cycles (in average, you move half the elements), but this is generally waived, for better or worse, when considering algorithms. If we consider moving elements, insertion sort is quadratic for the same reason. – Matthieu M. Feb 25 '18 at 14:48
2

This is an working java code where I filter out the brackets from the string expression and then check the well formedness by replacing wellformed braces by nulls

Sample input = (a+{b+c}-[d-e])+[f]-[g] FilterBrackets will output = ({}[])[][] Then I check for wellformedness.

Comments welcome.

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;
    }

}
Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
2

I doubt you'll find a better solution, since even if you use internal functions to regexp or count occurrences, they still have a O(...) cost. I'd say your solution is the best :)

To optimize for space you could do some run-length encoding on your stack, but I doubt it would gain you very much, except in cases like {{{{{{{{{{}}}}}}}}}}.

Svante
  • 50,694
  • 11
  • 78
  • 122
chris
  • 9,745
  • 1
  • 27
  • 27
  • He is talking about a better big-O space solution, not time. – Jeff B Mar 24 '10 at 16:39
  • I imagine this would be a good optimisation if you are counting parentheses in lisp – Douglas Mar 24 '10 at 16:51
  • If you're only looking at a single type of parenthesis, you can get away with O(log2 N) space (a counter, incremented for an open and decremented for a close). – Vatine Mar 29 '10 at 07:32
2

The following modification of Sbusidan's answer is O(n2) time complex but O(log n) space simple.

#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);
}
Community
  • 1
  • 1
2

http://www.sureinterview.com/shwqst/112007

It is natural to solve this problem with a stack.

If only '(' and ')' are used, the stack is not necessary. We just need to maintain a counter for the unmatched left '('. The expression is valid if the counter is always non-negative during the match and is zero at the end.

In general case, although the stack is still necessary, the depth of the stack can be reduced by using a counter for unmatched braces.

puttyshell
  • 21
  • 1
1

I know I'm a little late to this party; it's also my very first post on StackOverflow.

But when I looked through the answers, I thought I might be able to come up with a better solution.

So my solution is to use a few pointers.
It doesn't even have to use any RAM storage, as registers can be used for this.
I have not tested the code; it's written it on the fly.
You'll need to fix my typos, and debug it, but I believe you'll get the idea.

Memory usage: Only the CPU registers in most cases.
CPU usage: It depends, but approximately twice the time it takes to read the string.
Modifies memory: No.

b: string beginning, e: string end.
l: left position, r: right position.
c: char, m: match char

if r reaches the end of the string, we have a success.
l goes backwards from r towards b.
Whenever r meets a new start kind, set l = r.
when l reaches b, we're done with the block; jump to beginning of next block.

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 */
}

After thinking about this approach for a while, I realized that it will not work as it is right now.
The problem will be that if you have "[()()]", it'll fail when reaching the ']'.
But instead of deleting the proposed solution, I'll leave it here, as it's actually not impossible to make it work, it does require some modification, though.

  • A hint on how to make it work: You need to think outside the box for a moment. Don't think "stacking", but instead "skip-count"; eg how many times you need to skip back, before you start comparing. It's too complicated piece of code to post here, but now you should have enough inspiration to get started. –  Feb 13 '13 at 20:52
1

If you can overwrite the input string (not reasonable in the use cases I envision, but what the heck...) you can do it in constant space, though I believe the time requirement goes up to O(n2).

Like this:

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

The essence of this is to use the early part of the input as the stack.

Svante
  • 50,694
  • 11
  • 78
  • 122
dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
0
/**
 *
 * @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;
}

}
Botz3000
  • 39,020
  • 8
  • 103
  • 127
  • 1
    Thanks for posting an answer! While a code snippet could answer the question it's still great to add some addition information around, like explain, etc .. – j0k Oct 20 '12 at 11:29
0

Instead of putting braces into the stack, you could use two pointers to check the characters of the string. one start from the beginning of the string and the other start from end of the string. something like

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;
}

Note that there are some corner case not handled.

0

I think that you can implement an O(n) algorithm. Simply you have to initialise an counter variable for each type: curly, square and normal brackets. After than you should iterate the string and should increase the coresponding counter if the bracket is opened, otherwise to decrease it. If the counter is negative return false. AfterI think that you can implement an O(n) algorithm. Simply you have to initialise an counter variable for each type: curly, square and normal brackets. After than you should iterate the string and should increase the coresponding counter if the bracket is opened, otherwise to decrease it. If the counter is negative return false. After you count all brackets, you should check if all counters are zero. In that case, the string is valid and you should return true.

Svetlin Ralchev
  • 614
  • 6
  • 5
0

This is my solution to the problem. O(n) is the complexity of time without complexity of space. Code in 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;
}
SBusidan
  • 1
  • 1
0

You could provide the value and check if its a valid one, it would print YES otherwise it would print 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();

        }
Maxymus
  • 1,460
  • 2
  • 14
  • 22
0

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's 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's after the nest 
        if (test.pos.length && !recursiveCheck(test.pos)) return false;
        // recursive checks  what's 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);
};
rafaelcastrocouto
  • 11,781
  • 3
  • 38
  • 63
0

Using c# OOPS programming... Small and simple solution

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();
Jaydeep Shil
  • 1,894
  • 22
  • 21