2

私がやろうとしているのは、括弧が正しい順序であるかどうかを判断することです。たとえば、([][[]]<<>>)有効ですが、そうで][]<<(>>)はありません。

動作するバージョンを入手しましたが、効率が悪く、1000以上のブラケットを取得すると、非常に遅くなります。誰かが可能な改善点や別の方法を提案してくれることを望んでいました。

これが私のコードです:

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.

ファイル zavorky.in の入力データは、たとえば次のようになります。

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

行ごとに有効かどうかを判断します。1 行の括弧の最大数は 10000 です。

4

3 に答える 3

3

ファイルから文字を読み取ります。バイト単位モードでのファイルの読み取りは非常に低速です。代わりに文字列 (バッファ) を読み取る方法を最適化するか、最初にファイルをメモリにロードする必要があります。

以下では、取得した文字列を処理する別の方法を提案します。

最初に、あなたが持っている可能性のあるブラケットを示す const を宣言します。

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

括弧式の長さや括弧の種類の数に制限されなくなったので、これを行うことにしました。すべての閉じ括弧と対応する開き括弧のインデックスの差は 10 です。

関数のコードは次のとおりです。

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;

おそらく、バイト配列を作成することで余分な作業を行いますが、この方法は、i) より理解しやすく、ii) 使用例やチェックbegin/endブラケットなどのブラケット式の長さを変更できるため、柔軟性があるようです。

添付

主な問題がファイルのブロック読み取りを整理することにあることがわかり次第、ここでそれを行う方法のアイデアを示します。

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;
于 2015-11-22T19:11:37.700 に答える
0

以下は機能するはずで、 O(n)の順序になると思います。 n は文字列の長さです。最初に 2 つの関数を作成します。

IsLeft(bra : TBracket)は、ブラケットが左ブラケットか右ブラケットかを判別できるため、IsLeft('<') = TRUE、IsLeft('>>') = FALSE です。

IsMatchingPair(bra, ket : TBracket)は、2 つのブラケットが同じ「タイプ」であるかどうかを判断できるため、IsMatchingPair('(',')') = TRUE ですが、IsMatchingPair('{','>>') = FALSE になります。

次に、プロシージャ Push(bra : TBracket)関数 Pop : TBracket、および関数 IsEmpty : booleanの 3 つの関数を含むスタックTBracketStackを構築します。

これで、次のアルゴリズムが機能するはずです (予期せず文字列の末尾に落ちないようにするために、少し追加のコードが必要です)。

 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;
于 2015-11-22T21:56:22.183 に答える