it-swarm.com.de

Wie finde ich die Gültigkeit einer Folge von Klammern, geschweiften Klammern und eckigen Klammern?

Ich bin kürzlich auf dieses interessante Problem gestoßen. Sie erhalten eine Zeichenfolge, die nur die Zeichen '(', ')', '{', '}', '[' Und ']' Enthält. Beispiel: "[{()}]", Sie müssen eine Funktion schreiben, die die Gültigkeit einer solchen Eingabezeichenfolge überprüft. Die Funktion kann folgendermaßen aussehen:
bool isValid(char* s);
Diese Klammern müssen in der richtigen Reihenfolge geschlossen werden, zum Beispiel sind "()" und "()[]{}" alle gültig, aber "(]", "([)]" und "{{{{" sind nicht!

Ich kam mit folgenden O(n) Zeit und O(n) Raum Komplexität Lösung, die gut funktioniert:

  1. Behalten Sie einen Stapel Zeichen bei.
  2. Wann immer Sie öffnende Klammern finden '(', '{' OR '[' Schieben Sie sie auf den Stapel.
  3. Wenn Sie schließende Klammern ')', '}' OR ']') Finden, prüfen Sie, ob die Stapeloberseite der öffnenden Klammer entspricht der Stapel, sonst brechen Sie die Schleife und geben false zurück.
  4. Wiederholen Sie die Schritte 2 bis 3 bis zum Ende der Zeichenfolge.

Dies funktioniert, aber können wir es für den Raum optimieren, kann es ein konstanter zusätzlicher Raum sein, ich verstehe, dass die zeitliche Komplexität nicht geringer sein kann als O(n), da wir uns jedes Zeichen ansehen müssen.

Meine Frage ist also, können wir dieses Problem im O(1) Raum lösen?

46
Rajendra Uppal

Tatsächlich gibt es einen deterministischen Log-Space-Algorithmus aufgrund von Ritchie und Springsteel: http://dx.doi.org/10.1016/S0019-9958(72)90205-7 (entschuldigung nicht online). Da wir zur Indizierung der Zeichenfolge Log-Bits benötigen, ist dies platzoptimal.


Wenn Sie bereit sind, einen einseitigen Fehler zu akzeptieren, gibt es einen Algorithmus, der n Polylog (n) time und Polylog (n) space verwendet: http://www.eccc.uni-trier.de/report/2009/119/

11
user287792

In Bezug auf die exzellente Antwort aus Matthieu M. ist hier eine Implementierung in C #, die sehr gut zu funktionieren scheint.

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

Im Wesentlichen ist es lediglich erforderlich, Paare von Klammern zu entfernen, bis keine mehr zu entfernen sind. Wenn etwas übrig bleibt, sind die Klammern nicht gut geformt.

Beispiele für wohlgeformte Brackets:

()[]
{()[]}

Beispiel für fehlerhafte Klammern:

([)]
{()[}]
12
Contango

Wenn die Eingabe schreibgeschützt ist, glaube ich nicht, dass wir O(1) als Leerzeichen verwenden können. Es ist eine bekannte Tatsache, dass jede für den O(1) - Raum entscheidbare Sprache regulär ist (d. H. Als regulärer Ausdruck geschrieben werden kann). Ihr Satz von Zeichenfolgen ist keine reguläre Sprache.

Natürlich geht es hier um eine Turingmaschine. Ich würde erwarten, dass dies auch für fest installierte Word RAM -Maschinen gilt. 

6
Aryabhatta

Edit: Obwohl dieser Algorithmus einfach ist, ist er in Bezug auf Zeichenvergleiche eigentlich O (n ^ 2). Um dies zu demonstrieren, kann man einfach einen String als '(' * n + ')' * n generieren.

Ich habe eine einfache, wenn auch vielleicht falsche Vorstellung, die ich Ihrer Kritik unterwerfen werde.

Es ist ein destruktiver Algorithmus, dh wenn Sie die Zeichenfolge jemals benötigen, würde dies nicht helfen (da Sie sie dann abschreiben müssten).

Ansonsten arbeitet der Algorithmus mit einem einfachen Index innerhalb der aktuellen Zeichenfolge.

Die Idee ist, Paare nacheinander zu entfernen:

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

Es basiert auf der einfachen Tatsache, dass, wenn wir übereinstimmende Paare haben, mindestens eines der Form () ohne ein Paarzeichen dazwischen ist.

Algorithmus:

  1. i := 0
  2. Finden Sie ein passendes Paar in i. Wenn keine gefunden wird, ist die Zeichenfolge nicht gültig. Wenn einer gefunden wird, sei i der Index des ersten Zeichens.
  3. Entfernen Sie [i:i+1] aus der Zeichenfolge
  4. Wenn sich i am Ende der Zeichenfolge befindet und die Zeichenfolge nicht leer ist, ist dies ein Fehler.
  5. Wenn [i-1:i] ein übereinstimmendes Paar ist, i := i-1 und zurück zu 3.
  6. Sonst zurück zu 1.

Der Algorithmus ist O(n) in der Komplexität, weil:

  • bei jeder Wiederholung der Schleife werden 2 Zeichen aus der Zeichenfolge entfernt
  • der Schritt 2., der linear ist, ist natürlich gebunden (i kann nicht unendlich wachsen)

Und es ist O(1) im Weltraum, da nur der Index benötigt wird.

Wenn Sie es sich nicht leisten können, die Zeichenfolge zu zerstören, müssen Sie sie natürlich kopieren, und das ist O(n) im Weltraum.

Außer natürlich irre ich mich irgendwo zutiefst ... und vielleicht könnte jemand die ursprüngliche Idee (es gibt ein Paar irgendwo) für eine bessere Wirkung verwenden.

3
Matthieu M.

Dies ist ein funktionierender Java-Code, in dem ich die Klammern aus dem Zeichenfolgenausdruck herausfiltern und dann die Formung prüfen werde, indem ich wohlgeformte Klammern durch Nullen ersetzt

Das Beispiel input = (a+{b+c}-[d-e])+[f]-[g] FilterBrackets wird = ({}[])[][] ausgegeben. Dann überprüfe ich, ob die Form korrekt ist.

Kommentare sind willkommen.

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

}
2

Ich bezweifle, dass Sie eine bessere Lösung finden, denn selbst wenn Sie interne Funktionen verwenden, um Ereignisse auszudrücken oder zu zählen, haben sie immer noch O(...) Kosten. Ich würde sagen, deine Lösung ist die beste :)

Um den Speicherplatz zu optimieren, können Sie einige Lauflängencodierungen auf Ihrem Stack durchführen, aber ich bezweifle, dass Sie davon sehr profitieren würden, außer in Fällen wie {{{{{{{{{{}}}}}}}}}}.

2
chris

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

Es ist natürlich, dieses Problem mit einem Stapel zu lösen.

Wenn nur '(' und ')' verwendet wird, ist der Stack nicht erforderlich. Wir müssen nur einen Zähler für das nicht übereinstimmende linke '(') beibehalten. Der Ausdruck ist gültig, wenn der Zähler während der Übereinstimmung immer nicht negativ ist und am Ende Null ist.

Obwohl der Stapel immer noch erforderlich ist, kann die Tiefe des Stapels im Allgemeinen reduziert werden, indem ein Zähler für nicht übereinstimmende Klammern verwendet wird. 

2
puttyshell

Die folgende Änderung der Antwort von Sbusidan ist O (n2) zeit komplex aber O (log n) Platz einfach.

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

Wenn Sie die Eingabezeichenfolge überschreiben können (in den von mir angenommenen Anwendungsfällen nicht sinnvoll, aber was solls ...), können Sie dies im konstanten Raum tun, obwohl ich glaube, dass die Zeitanforderung bis O beträgt (n2) .

So was:

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

Das Wesentliche dabei ist, den frühen Teil der Eingabe als Stack zu verwenden.

1
dmckee

Ich weiß, dass ich zu dieser Party etwas spät komme. Es ist auch mein allererster Beitrag zu StackOverflow.

Aber als ich die Antworten durchgesehen hatte, dachte ich, ich könnte vielleicht eine bessere Lösung finden.

Meine Lösung besteht also darin, ein paar Hinweise zu verwenden.
Es muss nicht einmal ein Speicherplatz RAM verwendet werden, da hierfür Register verwendet werden können.
Ich habe den Code nicht getestet; Es ist im Fluge geschrieben.
Sie müssen meine Tippfehler korrigieren und sie debuggen, aber ich glaube, Sie werden die Idee bekommen.

Speicherbelegung: In den meisten Fällen wird nur die CPU registriert.
CPU-Auslastung: Dies hängt davon ab, aber ungefähr die doppelte Zeit, um die Zeichenfolge zu lesen.
Ändert den Speicher: Nein.

b: string beginning, e: string end.
l: left-Position, r: rrechte Position.
c: char, m: match char

wenn r das Ende der Zeichenfolge erreicht, haben wir einen Erfolg.
l geht rückwärts von r nach b.
.__ Wenn immer r eine neue Startart trifft, setze l = r.
Wenn ich b erreicht, sind wir mit dem Block fertig. zum Anfang des nächsten Blocks springen.

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

Nachdem ich eine Weile über diesen Ansatz nachgedacht hatte, wurde mir klar, dass er nicht so funktionieren wird, wie es jetzt ist.
Das Problem wird sein, dass, wenn Sie "[() ()]" haben, dies beim Erreichen von ']' fehlschlägt.
Aber anstatt die vorgeschlagene Lösung zu löschen, lasse ich sie hier, da es eigentlich nicht unmöglich ist, sie zum Laufen zu bringen, es bedarf jedoch einiger Änderungen.

1
user1985657

Mit c # OOPS programmieren ... Kleine und einfache Lösung

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();
0
Jaydeep Shil

Dies ist meine Lösung für das Problem . O (n) ist die Komplexität der Zeit ohne Komplexität des Platzes . 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;
}
0
SBusidan
/**
 *
 * @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;
}

}
0

Sie können den Wert angeben und prüfen, ob er gültig ist. JA würde gedruckt, ansonsten NEIN

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();

        }
0
Maxymus

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

0

Anstatt geschweifte Klammern in den Stapel zu setzen, können Sie die Zeichen der Zeichenfolge mit zwei Zeigern überprüfen. einer beginnt am Anfang der Zeichenfolge und der andere beginnt am Ende der Zeichenfolge. so etwas wie

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

Beachten Sie, dass einige Eckfälle nicht behandelt werden.

0
Jiangbo

Ich denke, dass Sie einen O(n) - Algorithmus implementieren können. Sie müssen einfach eine Zählervariable für jeden Typ initialisieren: geschweifte, eckige und normale Klammern. Danach sollten Sie die Zeichenfolge durchlaufen und den entsprechenden Zähler erhöhen, wenn die Klammer geöffnet wird. Andernfalls kann die Zeichenfolge verringert werden. Wenn der Zähler negativ ist, geben Sie false zurück. Danach glaube ich, dass Sie einen O(n) - Algorithmus implementieren können. Sie müssen einfach eine Zählervariable für jeden Typ initialisieren: geschweifte, eckige und normale Klammern. Danach sollten Sie die Zeichenfolge durchlaufen und den entsprechenden Zähler erhöhen, wenn die Klammer geöffnet wird. Andernfalls kann die Zeichenfolge verringert werden. Wenn der Zähler negativ ist, geben Sie false zurück. Nachdem Sie alle Klammern gezählt haben, sollten Sie prüfen, ob alle Zähler Null sind. In diesem Fall ist die Zeichenfolge gültig und Sie sollten true zurückgeben.

0
Svetlin Ralchev