2

what I am trying to do, is determine, whether brackets are in correct order. For example ([][[]]<<>>) is vallid, but ][]<<(>>) is not.

I got a working version, but it has terrible efficiency and when it gets 1000+ brackets, its just crazy slow. I was hoping someone might suggest some possible improvements or another way to do it.

Here is my code:

program Codex;

const
    C_FNAME = 'zavorky.in';

var TmpChar     : char;
    leftBrackets, rightBrackets : string;
    bracketPos         : integer;
    i,i2,i3         : integer;
    Arr, empty : array [0..10000] of String[2];
    tfIn    : Text;
    result : boolean;

begin
    leftBrackets := ' ( [ /* ($ <! << ';
    rightBrackets := ' ) ] */ $) !> >> ';
    i := 0;
    result := true;
    Assign(tfIn, C_FNAME);
    Reset(tfIn);

    { load data into array }
    while not eof(tfIn) do
    begin
        while not eoln(tfIn) do
        begin
            read(tfIn, TmpChar);
            if (TmpChar <> ' ') then begin
                if (TmpChar <> '') then begin
                    Arr[i] := Arr[i] + TmpChar;
                    end
                end
            else
                begin                                       
                    i := i + 1;
                end
        end;

        i2 := -1;
        while (i2 < 10000) do begin     
            i2 := i2 + 1;
            {if (i2 = 0) then
                writeln('STARTED LOOP!');}
            if (Arr[i2] <> '') then begin
                bracketPos := Pos(' ' + Arr[i2] + ' ',rightBrackets);
                if (bracketPos > 0) then begin
                    if (i2 > 0) then begin
                        if(bracketPos = Pos(' ' + Arr[i2-1] + ' ',leftBrackets)) then begin
                            {write(Arr[i2-1] + ' and ' + Arr[i2] + ' - MATCH ');}

                            Arr[i2-1] := '';
                            Arr[i2] := '';
                            { reindex our array }
                            for i3 := i2 to 10000 - 2 do begin
                                Arr[i3 - 1] := Arr[i3+1];
                                end;

                            i2 := -1;
                            end;
                        end;                    
                    end;
                end;
            end;

        {writeln('RESULT: ');}
        For i2:=0 to 10 do begin
            if (Arr[i2] <> '') then begin
                {write(Arr[i2]);}
                result := false;
            end;
            {else
            write('M');}
        end;

        if (result = true) then begin
            writeln('true');
            end
        else begin
            writeln('false');
        end;

        result := true;

        { move to next row in file }
        Arr := empty;
        i := 0;
        readln(tfIn);
    end;

    Close(tfIn);

    readln;
end.

The input data in the file zavorky.in look for example like this:

<< $) >> << >> ($ $) [ ] <! ( ) !>
( ) /* << /* [ ] */ >> <! !> */

I determine for each row whether it is valid or not. Max number of brackets on a row is 10000.

3 Answers3

3

You read chars from your file. File read in byte-by-byte mode is very slow. You need to optimize the way to read the strings (buffers) instead or load the file in memory first.

Hereunder I propose the other way to process the fetched string.

First I declare the consts that will state the brackets that you might have:

const
  OBr: array [1 .. 5{6}]   of string = ('(', '[', '/*', '<!', '<<'{, 'begin'});
  CBr: array [11 .. 15{16}] of string = (')', ']', '*/', '!>', '>>'{, 'end'});

I decided to do this as now you are not limited to the length of the brackets expression and/or number of brackets' types. Every closing and corresponding opening bracket has index difference equal to 10.

And here is the code for the function:

function ExpressionIsValid(const InputStr: string): boolean;
var
  BracketsArray: array of byte;
  i, Offset, CurrPos: word;
  Stack: array of byte;
begin
  result := false;
  Setlength(BracketsArray, Length(InputStr) + 1);
  for i := 0 to High(BracketsArray) do
    BracketsArray[i] := 0; // initialize the pos array

  for i := Low(OBr) to High(OBr) do
  begin
    Offset := 1;
    Repeat
      CurrPos := Pos(OBr[i], InputStr, Offset);
      if CurrPos > 0 then
      begin
        BracketsArray[CurrPos] := i;
        Offset := CurrPos + 1;
      end;
    Until CurrPos = 0;
  end; // insert the positions of the opening brackets

  for i := Low(CBr) to High(CBr) do
  begin
    Offset := 1;
    Repeat
      CurrPos := Pos(CBr[i], InputStr, Offset);
      if CurrPos > 0 then
      begin
        BracketsArray[CurrPos] := i;
        Offset := CurrPos + 1;
      end;
    Until CurrPos = 0;
  end; // insert the positions of the closing brackets

  Setlength(Stack, 0); // initialize the stack to push/pop the last bracket
  for i := 0 to High(BracketsArray) do
    case BracketsArray[i] of
      Low(OBr) .. High(OBr):
        begin
          Setlength(Stack, Length(Stack) + 1);
          Stack[High(Stack)] := BracketsArray[i];
        end; // there is an opening bracket
      Low(CBr) .. High(CBr):
        begin
          if Length(Stack) = 0 then
            exit(false); // we can not begin an expression with Closing bracket
          if Stack[High(Stack)] <> BracketsArray[i] - 10 then
            exit(false) // here we do check if the previous bracket suits the
                        // closing bracket
          else
            Setlength(Stack, Length(Stack) - 1); // remove the last opening
                                                 // bracket from stack
        end;
    end;
  if Length(Stack) = 0 then
    result := true;
end;

Perhaps, we do an extra work by creating a byte array, but it seems that this method is i) more easy to understand and ii) is flexible as we can change the length of brackets expressions for example use and check begin/end brackets etc.

Appended

As soon as I see that the major problem is in organizing block reading of file I give here an idea of how to do it:

procedure BlckRead;
var
  f: file;
  pc, pline: { PChar } PAnsiChar;
  Ch: { Char } AnsiChar;
  LngthLine, LngthPc: word;
begin
  AssignFile(f, 'b:\br.txt');   //open the file
  Reset(f, 1);
  GetMem(pc, FileSize(f) + 1);  //initialize memory blocks
  inc(pc, FileSize(f)); //null terminate the string
  pc^ := #0;
  dec(pc, FileSize(f)); //return the pointer to the beginning of the block

  GetMem(pline, FileSize(f)); //not optimal, but here is just an idea.
  pline^ := #0;//set termination => length=0
  BlockRead(f, pc^, FileSize(f)); // read the whole file
                                  //you can optimize that if you wish,
                                  //add exception catchers etc.
  LngthLine := 0; // current pointers' offsets
  LngthPc := 0;
  repeat
    repeat
      Ch := pc^;
      if (Ch <> #$D) and (Ch <> #$A) and (Ch <> #$0) then
      begin // if the symbol is not string-terminating then we append it to pc
        pline^ := Ch;
        inc(pline);
        inc(pc);
        inc(LngthPc);
        inc(LngthLine);
      end
      else
      begin //otherwise we terminate pc with Chr($0);
        pline^ := #0;
        inc(LngthPc);
        if LngthPc < FileSize(f) then
          inc(pc);
      end;
    until (Ch = Chr($D)) or (Ch = Chr($A)) or (Ch = Chr($0)) or
      (LngthPc = FileSize(f));

    dec(pline, LngthLine);
    if LngthLine > 0 then //or do other outputs
      Showmessage(pline + #13#10 + Booltostr(ExpressionIsValid(pline), true));

    pline^ := #0; //actually can be skipped but you know your file structure better
    LngthLine := 0;
  until LngthPc = FileSize(f);

  FreeMem(pline);                          //free the blocks and close the file
  dec(pc, FileSize(f) - 1);
  FreeMem(pc);
  CloseFile(f);
end;
asd-tm
  • 3,381
  • 2
  • 24
  • 41
  • But the thing is, that since one row of brackets can be up to 10.000 brackets long, I can't read it in strings, since it won't fit. And dividing the 10.000 chars into strings won't work welll with the checks.. – Mykybo Nov 23 '15 at 19:05
  • @Mykybo Actually, it is not a problem. String in Pascal is like an array of char. Declare an array of char and fill it from file. Then use the idea I have given you here. If it is necessary, you can also redeclare a function like `Pos` yourself. But, please, do not read your files byte-byte-byte. It is very slow. – asd-tm Nov 23 '15 at 20:25
  • @Mykybo I have appended the answer. See the idea how to make the block read of your file. – asd-tm Nov 24 '15 at 08:34
1

You are saving all the data into memory (even couple of times) and then you have a lot of checks. I think you are on the right track but there are much easier steps you could follow.

  1. Make an array of integers (default = 0) with length the number of brackets you have (e.g. ' ( [ /* ($ <! << ' ==> 6)
  2. Now to make sure that you are following the requirements. Read the file line by line and take into account only the first 10000. This could help.
  3. Every time you find an element from the first array (e.g. leftBrackets) add +1 to the value of the coresponding index of the array of step 1. Example would be: '[' ==> checkArray[1] += 1
  4. Do the same for rightBrackets but this time check if the value is larger than 0. If yes then subtract 1 the same way (e.g. ']' ==> checkArray[1] -= 1) otherwise you just found invalid bracket

I hope this helps and Good luck.

  • If I am not mistaken, this would fail in this case: [(]) , which is invalid, but the way you suggested would make it look valid. – Mykybo Nov 22 '15 at 09:47
  • You are completely right. I am sorry about that. To solve this the easiest way would be to implement a stack. When there is a leftBracket `push` the index of the corresponding leftBracket array and if there is rightBracket `pop` and check if the variables are the same. If they mismatch or the stack is empty then you just found invalid bracket sequence –  Nov 22 '15 at 10:05
0

I think the following should work, and will be order O(n), where n is the length of the string. First build two function.

IsLeft(bra : TBracket) can determine if a bracket is a left bracket or a right bracket, so IsLeft('<') = TRUE, IsLeft('>>') = FALSE.

IsMatchingPair(bra, ket : TBracket) can determine if two brackets are of the same 'type', so IsMatchingPair('(',')') =TRUE, but IsMatchingPair('{','>>') = FALSE.

Then build a stack TBracketStack with three functions procedure Push(bra : TBracket), and function Pop : TBracket, and function IsEmpty : boolean.

Now the following algorithm should work (with a little extra code required to ensure you don't fall off the end of the string unexpectedly):

 BracketError := FALSE;
 while StillBracketsToProcess(BracketString) and not BracketError do
 begin
   bra := GetNextBracket(BracketString);
   if IsLeft(bra) then 
     Stack.Push(bra) 
   else
     BracketError := Stack.IsEmpty or not IsMatchingPair(Stack.Pop,bra)
 end;
Penguino
  • 2,136
  • 1
  • 14
  • 21