Проверьте правильность порядка скобок
То, что я пытаюсь сделать, это определить, в правильном ли порядке скобки. Например ([][[]]<<>>)
блеклый, но ][]<<(>>)
не является.
Я получил рабочую версию, но она имеет ужасную эффективность, и когда она получает более 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 выглядят, например, так:
<< $) >> << >> ($ $) [ ] <! ( ) !>
( ) /* << /* [ ] */ >> <! !> */
Я определяю для каждой строки, является ли она действительной или нет. Максимальное количество скобок в ряду - 10000.
3 ответа
Вы читаете символы из своего файла. Файл читается в побайтовом режиме очень медленно. Вам нужно оптимизировать способ чтения строк (буферов) вместо этого или сначала загрузить файл в память.
Ниже я предлагаю другой способ обработки извлеченной строки.
Сначала я объявляю константы, в которых будут указаны скобки:
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;
Вы сохраняете все данные в памяти (даже пару раз), а затем у вас есть много проверок. Я думаю, что вы на правильном пути, но есть гораздо более простые шаги, которые вы могли бы выполнить.
- Создайте массив целых чисел (по умолчанию = 0) с длиной, равной количеству скобок (например, '
( [ /* ($ <! << ' ==> 6
) - Теперь, чтобы убедиться, что вы следуете требованиям. Читайте файл построчно и учитывайте только первые 10000. Это может помочь.
- Каждый раз, когда вы находите элемент из первого массива (например, leftBrackets), добавьте +1 к значению индекса отклика ядра массива шага 1. Примером может быть:
'[' ==> checkArray[1] += 1
- Сделайте то же самое для rightBrackets, но на этот раз проверьте, если значение больше 0. Если да, то вычтите 1 таким же образом (например,
']' ==> checkArray[1] -= 1
) в противном случае вы только что нашли неверную скобку
Надеюсь, это поможет и удачи.
Я думаю, что следующее должно работать, и будет порядок O (n), где n - длина строки. Сначала постройте две функции.
IsLeft (бюстгальтер: TBracket) может определить, является ли скобка левой или правой скобкой, поэтому IsLeft ('<') = TRUE, IsLeft ('>>') = FALSE.
IsMatchingPair (bra, ket: TBracket) может определить, имеют ли две скобки одинаковый тип, поэтому IsMatchingPair('(',')') =TRUE, но IsMatchingPair('{','>>') = FALSE.
Затем создайте стек TBracketStack с тремя функциями: процедура Push (бюстгальтер: TBracket), функция Pop: TBracket и функция IsEmpty: boolean.
Теперь следующий алгоритм должен работать (с небольшим дополнительным кодом, необходимым для того, чтобы вы неожиданно не упали с конца строки):
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;