ACM ICPC 20062007, NEERC, Moscow Subregional Contest
Moscow, October 21, 2006
Problem C. Code Checker
Time limit:
3 seconds
Memory limit:
64 MiB
Prof. Acmeroff teaches programming in the Institute of Business Management. He teaches a relatively new,
but popular language called Db. Unfortunately, program plagiarism is a serious headache for the professor,
because some students cheat when doing home exersises. So the professor has to check students' works for
plagiatrism each time. Thus a computer program, which is able to perform some sort of program comparison
might be very useful for him.
The Db language is briefly described as follows:
· The # character never appears in programs. The character \ never appears in programs outside of
strings.
· All characters with codes less or equal then 32 are whitespace characters. A sequence of whitespace
characters is called whitespace and equivalent to one space character (code 32).
· Comments starting from /* run till the first sequence of */ characters.
· A comment is equivalent to one space character.
· String literals start from " and run till the next ", except \".
· An identifier is the longest possible sequence of characters starting from latin letter or underscore
and containing latin letters, underscore and digits. For example, sequence (0x800+) contains one
identifier x800. Letter case is significant.
· Some indentifiers are reserved as keywords. The exact list of keywords depends on the language
dialect.
· There are no lexical scoping rules.
A program is processed as follows:
1. Each comment is replaced with a single space character.
2. Each whitespace (including space characters from the previous step) is replaced with a signle space
character.
3. Each string is replaced with "".
4. Space characters are removed, except those separating identifiers and keywords from each other.
Finally, if there exists an one-to-one correspondence between the identifiers of the first program and the
indentifiers of the second program, so the replacement of the identifiers in the second program with the
corresponding identifiers of the first program yelds two equal texts, such programs are called identical.
Write a program, which checks whether two given programs written in Db are identical.
Input
The input for your program consists of the list of keywords and two programs written in Db. The list of
keywords and the programs are separated from each other with a single # character. The list of keywords
section only contains keywords and whitespace. The size of input does not exceed 4MiB. The total number
of different identifiers and keywords does not exceed 65535.
Page 4 of 17
ACM ICPC 20062007, NEERC, Moscow Subregional Contest
Moscow, October 21, 2006
Output
Print the word YES, if the input programs are identical, and the word NO otherwise.
Example
stdin
stdout
int
void
YES
while if return
#
/* sizeof(int) == 4 */
int main(void) {
int max = 0x80000000, n;
while(scanf("%d", &n) == 1)
if (n > max) max = n;
printf("%d", max);
return 0;
}
#
/* sizeof(int) == 4 */
int n(void) {
int max = 0x80000000, main;
while(printf("%d", &main) ==
1)
if(main > max)
max = main;
scanf("Answer: \"%d\"", max);
return 0;
}
Page 5 of 17
решение на паскале ( fpc ) :
- Код: Выделить всё
- program array1;
 {$mode objfpc}
 uses crt,sysutils;
 
 type
 pget = function() : byte;
 pa = procedure(iii : byte);
 pliteral = function(var a : ansistring): boolean;
 endloop = class(exception);
 ss = set of char;
 var
 dl,sid,id : ss;
 a1 : array [1..100] of string;
 bl : boolean;
 pl : pliteral;
 file1,file2 : AnsiString;
 tstr : string;
 wnum,knum : dword; { number of indifier+keyword,keyword }
 pread : pget;
 padd : pa;
 iii,i,ii : dword;
 c : char;
 b : byte;
 loop : dword;
 f : file of byte;
 procedure add1(b1 :byte); forward;
 procedure add2(b1 :byte); forward;
 function get2() : byte; forward;
 function checkend(b1 : byte) : boolean;
 var
 temp : boolean;
 begin
 temp := ( filepos(f) >= filesize(f)) or ( b1 = byte( '#')) ;
 if temp then loop :=1;
 checkend := temp;
 end;
 function get1() : byte;
 begin
 read(f,b);
 checkend(b);
 if b>32 then
 begin
 pread := @get2;
 padd := @add2;
 end;
 get1 := b;
 end;
 function get2() : byte;
 begin
 read(f,b);
 checkend(b);
 if b<33 then
 begin
 pread := @get1;
 padd := @add1;
 wnum += 1;
 end;
 get2 :=b;
 end;
 procedure add1(b1 :byte);
 begin
 end;
 procedure add2(b1 :byte);
 begin
 tstr := char(b1);
 a1[wnum] += tstr;
 end;
 function l2(var a : ansistring) : boolean ; forward;
 function l3(var a : ansistring) : boolean ; forward;
 function l1(var a : ansistring) : boolean ;
 begin
 i := length( a);
 bl := false;
 loop := 0;
 while ii<=i do
 
 begin
 if a[ii] = '"' then
 begin
 bl := true;
 ii+=1;
 loop := 1;
 pl := @l2;
 break;
 end;
 ii+=1;
 end;
 i := ii;
 l1 := bl;
 end;
 function l2(var a : ansistring) : boolean ;
 begin
 if ( a[ii-1] = '\') then
 else
 if (a[ii] = '"') then pl:=@l3;
 ii+=1;
 l2:=true
 
 end;
 function l3(var a : ansistring) : boolean ;
 begin
 delete(a,i,ii-1-i);
 pl := @l1;
 ii := i+1;
 l3:=true;
 end;
 function l5(var a : ansistring) : boolean ; forward;
 function l7(var a : ansistring) : boolean ; forward;
 function l4(var a : ansistring) : boolean ;
 begin
 i+=1;
 l4 := false;
 if a[i] in id then
 begin
 pl := @l7;
 if a[i] in sid then
 begin
 pl := @l5;{ id or kw begin }
 end;
 end;
 if i<length(a) then l4:= true;
 end;
 function l5(var a : ansistring) : boolean ;
 var tss : string;
 begin
 tstr:='';
 while a[i] in id do
 begin
 tstr+=a[i];
 i+=1;
 end;
 iii:=0;
 for ii:=1 to wnum do
 begin
 if a1[ii] = tstr then
 begin
 iii:=ii;
 {
 writeln('keyword :',tstr,' id:',iii);
 }
 i-=length(tstr);
 delete(a,i,length(tstr));
 str(iii,tss);
 insert('\',tss,1);
 insert(tss,a,i);
 i+=length(tss);
 
 break;
 end;
 end;
 if iii = 0 then
 begin
 wnum+=1;
 a1[wnum] := tstr;
 i-=length(tstr);
 delete(a,i,length(tstr));
 str(wnum,tss);
 insert('\',tss,1);
 insert(tss,a,i);
 i+=length(tss);
 writeln('indifier id = ',wnum,' : ',tstr);
 end;
 pl := @l4;
 l5:=true;
 end;
 function l7(var a : ansistring) : boolean;
 begin
 write('constant : ');
 while a[i] in id do
 begin
 write(a[i]);
 i+=1;
 end;
 writeln();
 pl:=@l4;
 l7:=true;
 end;
 procedure preformat( var a : ansistring);
 label j1,j2,j3,j4;
 begin
 { delete comments }
 goto j3;
 j4:
 delete(a,i,ii-i+2);
 j3:
 i:=pos('/*',a);
 ii:=pos('*/',a);
 if i*ii>0 then goto j4;
 { delete extra space }
 goto j1;
 j2:
 delete(a,i,1);
 j1:
 i:=pos(' ',a) ;
 if i>0 then goto j2;
 { delete literal }
 pl := @l1;
 loop := 1;
 ii := 1;
 while( loop > 0 ) do
 begin
 pl(a) ;
 end;
 end;
 { main }
 begin
 { init }
 assign(f,'pcre.c');
 reset(f);
 dl := [' '..'/',':'..'@','['..'`','{'..'~'];
 sid := ['_','A'..'Z','a'..'z'];
 id := sid + ['0'..'9'];
 { read keywords }
 pread := @get1;
 padd := @add1;
 wnum :=1;
 while loop =0 do
 begin
 b := pread();
 padd(b);
 end;
 for loop := 1 to wnum do writeln('keyword id = ',loop,' : ',a1[loop]) ;
 { read 1 program }
 repeat
 read(f,b);
 if b<32 then b :=32;
 tstr := char(b);
 file1 += tstr;
 insert(' ',file1,1);
 until b = 35 ;
 delete(file1,length(file1),1);
 preformat(file1);
 { indifier parse }
 ii :=2;
 i := 1;
 pl :=@l4;
 writeln('creating first program indifiers ... ');
 knum:=wnum;
 while pl(file1) do ii+=1;
 { read 2 program }
 repeat
 read(f,b);
 if b<32 then b :=32;
 tstr := char(b);
 file2 += tstr;
 until filepos(f) >= filesize(f) ;
 insert(' ',file2,1);
 preformat(file2);
 { indifier parse}
 writeln('creating second program indifiers ... ');
 { for i:=knum to wnum do a1[i] := '';}
 wnum:=knum;
 ii :=2;
 i := 1;
 pl :=@l4;
 knum:=wnum;
 while pl(file2) do ii+=1;
 writeln(file2);
 writeln(file1);
 if file1 = file2 then writeln('programs equals');
 end.
пример :
вход ::
- Код: Выделить всё
 printf return int
 if then
 #include <stdio.h>
 /* coment 1 */123
 include <pcre.h>
 int main() {
 pcre *p; /* comment 2 / * /
 sldkfls sdlfj; ljkkj sldkjflk */123
 / /
 printf("pcre: hello \" there\n");
 p=pcre_compile("lalalal",0,0,0,0);
 return 0;
 #include <stdio.h>
 /* coment 1 */123
 include <pcre55.h>
 int main() {
 pcre55 *p; /* comment 2 / * /
 sldkfls sdlfj; ljkkj sldkjflk */123
 / /
 printf("pcre: sldkfjlk kk hello \" there\n");
 p=pcre_compile("lalalal",0,0,0,0);
 return 0;
выход:
- Код: Выделить всё
- keyword id = 1 : printf
 keyword id = 2 : return
 keyword id = 3 : int
 keyword id = 4 : if
 keyword id = 5 : then
 keyword id = 6 : #
 creating first program indifiers ...
 indifier id = 7 : include
 indifier id = 8 : stdio
 indifier id = 9 : h
 constant : 123
 indifier id = 10 : pcre
 indifier id = 11 : main
 indifier id = 12 : p
 constant : 123
 indifier id = 13 : pcre_compile
 constant : 0
 constant : 0
 constant : 0
 constant : 0
 constant : 0
 creating second program indifiers ...
 indifier id = 7 : include
 indifier id = 8 : stdio
 indifier id = 9 : h
 constant : 123
 indifier id = 10 : pcre55
 indifier id = 11 : main
 indifier id = 12 : p
 constant : 123
 indifier id = 13 : pcre_compile
 constant : 0
 constant : 0
 constant : 0
 constant : 0
 constant : 0
 \7 <\8.\9> 123 \7 <\10.\9> \3 \11() { \10 *\12; 123 / / \1(""); \12=\13("",0,0,
 0,0); \2 0;
 \7 <\8.\9> 123 \7 <\10.\9> \3 \11() { \10 *\12; 123 / / \1(""); \12=\13("",0,0,
 0,0); \2 0;
 programs equals
на решение ушло:
- 3 часа на поиск юнита регулярных выражений
- 30 мин на изучение libboost - как оказалось написано полностью на с++
- 5 часов на изучение и попытку написать врапер к pcre - потом решил делать все вручную без бибилиотек, будет больше практики с паскалем
- 3 часа на разработку алгоритма ( выбрал за основу теорию автоматов )
- 8 часов на его реализацию, пока не понял что утратил контроль над кодом, теория автоматов плохо реализуется на паскале, во всяком случае после 32 часового знакомсва с ним
- пришлось сделать откат кода на 5 часов назад
- 6.5 часов разработка нового алгоритма ( теорию автоматов пришлось заменить на императивный, а потом процедурный стиль )
- 12 часов на реализацию
- отладки почему-то опять не потребовалось ... может быть потому-что я разучился отлаживать ?
зы я фигею за человека, способного решить эту задачу в реале на чемпионате за 2 часа !!!
несколько пришлось отойти от класического задания, тк не умел делат несколько фишек :
1. тк не знаю как считать eof со стандартного ввода, данные прога считывает из файла pcre.c
2. тк не знаю как на паскале делать релизную и отладочную версии, то отладочная информация выдается на экран
3. тк встроенные средсва языка плохо работают ( или я в них плохо разобрался ) со строками, то из-за не эффективного алгоритма прога выходит за рамки времени при 10-30 кб исходниках, при норме в 1мб кода
ну естесно накопились вопросы:
- как посмотреть узкое место выполнения?
- на базе чего удобнее решать эту задачу: теория автоматов, data-driven или что то еще ?
- где можно что нить найти по теории автоматов и по теории формальных грамматик для ньюба?





