uses
SysUtils;
type
IsAnagramFunc = function (const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
function IsAnagram(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
type
TFreq = array[#32..#127] of Integer;
const
AllowedChars = [#32..#127];
var
i,SpaceCnt: Integer;
F1, F2: TFreq;
Ch: Char;
begin
Result := False;
F1 := Default(TFreq);
SpaceCnt := 0;
for i := 1 to Length(S1) do
begin
Ch := LowerCase(S1[i]);
if (Ch in AllowedChars) then
begin
if (Ch = #32) then
Inc(SpaceCnt)
else
Inc(F1[Ch]);
end
else
begin
if ExceptionOnError then
Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d',[i]);
Exit;
end;
end;
F2 := Default(TFreq);
for i := 1 to Length(S2) do
begin
Ch := LowerCase(S2[i]);
if (Ch in AllowedChars) then
begin
if (Ch = #32) then
Dec(SpaceCnt)
else
Inc(F2[Ch]);
end
else
begin
if ExceptionOnError then
Raise ERangeError.CreateFmt('IsAnagram: illegal character in S2 at position %d',[i]);
Exit;
end;
end;
Result := IgnoreSpaces or (SpaceCnt = 0);
if Result then
Result := CompareMem(@F1, @F2, SizeOf(TFreq));
end;
function IsAnagram_fibo(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
var
F1, F2: array[33..122] of Byte;
i, SpaceCnt: Integer;
Ch1, Ch2: Byte;
begin
FillChar(F1, SizeOf(F1), 0);
FillChar(F2, SizeOf(F2), 0);
SpaceCnt := 0;
result := true;
for i := 1 to Length(S1) do begin
Ch1 := Ord(S1[i]);
if (Ch1 < 33) or (Ch1 > 122) then if (Ch1 <> 32) and IgnoreSpaces then if ExceptionOnError then
Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d', [i])
else exit;
if Ch1 = 32 then Inc(SpaceCnt)
else if (Ch1 >= 33) and (Ch1 <> 96) then if Ch1 <= 122 then Inc(F1[Ch1 or $20]) else Inc(F1[Ch1]);
end;
for i := 1 to Length(S2) do begin
Ch2 := Ord(S2[i]);
if (Ch2 < 33) or (Ch2 > 122) then if (Ch2 <> 32) and IgnoreSpaces then if ExceptionOnError then
Raise ERangeError.CreateFmt('IsAnagram: illegal character in S2 at position %d', [i])
else exit;
if Ch2 = 32 then Dec(SpaceCnt)
else if (Ch2 >= 33) and (Ch2 <> 96) then if Ch2 <= 122 then Inc(F2[Ch2 or $20]) else Inc(F2[Ch2]);
end;
result := (IgnoreSpaces or (SpaceCnt = 0)) and CompareMem(@F1, @F2, SizeOf(F1));
end;
function IsAnagramASerge(const S1, S2: string; IgnoreSpaces: Boolean = True;
ExceptionOnError: Boolean = False): Boolean;
procedure Error(const Where: string; AtPos: SizeInt); //noreturn;
begin
raise ERangeError.CreateFmt(
'IsAnagram: illegal character in %s at position %d', [Where, AtPos]);
end;
type
TFreq = array[33..127] of Integer;
function FillOk(const S: string; out Data: TFreq; out SpaceCnt, ErrPos: Integer): Boolean;
var
i: Integer;
B: Byte;
begin
FillChar(Data, SizeOf(Data), 0);
SpaceCnt := 0;
for i := 1 to Length(S) do
begin
B := Ord(S[i]);
case B of
32: Inc(SpaceCnt);
33..Pred(Ord('A')): Inc(Data[B]);
Ord('A')..Ord('Z'): Inc(Data[B or $20]);
Ord(Succ('Z'))..127: Inc(Data[B]);
else
ErrPos := i;
Exit(False);
end;
end;
ErrPos := 0;
Result := True;
end;
var
F1, F2: TFreq;
SpaceCnt1, SpaceCnt2, ErrPos: Integer;
begin
if not FillOk(S1, F1, SpaceCnt1, ErrPos) then
if ExceptionOnError then
Error('S1', ErrPos)
else
Exit(False);
if not FillOk(S2, F2, SpaceCnt2, ErrPos) then
if ExceptionOnError then
Error('S2', ErrPos)
else
Exit(False);
Result := IgnoreSpaces or (SpaceCnt1 = SpaceCnt2);
if Result then
Result := CompareMem(@F1, @F2, SizeOf(TFreq));
end;
// Zvoni
Var
os1,os2:String;
b:Boolean;
procedure QuickSort(var AI: array of Char; ALo, AHi: Integer);
var
Pivot,T: Char;
Lo, Hi:Integer;
begin
Lo := ALo;
Hi := AHi;
Pivot := AI[(Lo + Hi) div 2];
repeat
while AI[Lo] < Pivot do
Inc(Lo) ;
while AI[Hi] > Pivot do
Dec(Hi) ;
if Lo <= Hi then
begin
T := AI[Lo];
AI[Lo] := AI[Hi];
AI[Hi] := T;
Inc(Lo) ;
Dec(Hi) ;
end;
until Lo > Hi;
if Hi > ALo then
QuickSort(AI, ALo, Hi) ;
if Lo < AHi then
QuickSort(AI, Lo, AHi) ;
end;
Function StrSpn(Const str:PChar;Const Accept:PChar):Integer;
Var
a:PChar;
table:Array[0..255] Of Byte;
p:PByte;
c0,c1,c2,c3:ByteBool;
s:PChar;
Count:Integer;
Begin
If Accept[0]=#0 Then Exit(0);
If Accept[1]=#0 Then
Begin
a:=str;
While a^=accept^ Do Inc(a);
Exit(a-str);
end;
FillChar(table,64,0);
p:=@table[0];
FillChar(table[64],64,0);
FillChar(table[128],64,0);
FillChar(table[192],64,0);
s:=accept;
While s^<>#0 Do
Begin
p[Byte(s^)]:=1;
Inc(s);
end;
s:=str;
If Not ByteBool(p[Byte(s[0])]) Then Exit(0);
If Not ByteBool(p[Byte(s[1])]) Then Exit(1);
If Not ByteBool(p[Byte(s[2])]) Then Exit(2);
If Not ByteBool(p[Byte(s[3])]) Then Exit(3);
Repeat
Inc(s,4);
c0:=ByteBool(p[Byte(s[0])]);
c1:=ByteBool(p[Byte(s[1])]);
c2:=byteBool(p[Byte(s[2])]);
c3:=ByteBool(p[Byte(s[3])]);
until Not (c0 And C1 And C2 And C3);
Count:=s-str;
If Not (c0 And c1) Then
Result:=count+Byte(c0)
Else
Result:=Count+Byte(c2)+2;
End;
function IsAnagram_Zvoni(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
Var
l1,l2:Integer;
ps1,ps2:Array Of AnsiChar;
i:Integer;
p1,p2:PChar;
by:Byte;
c:Array[32..127] Of Char; //Legal Characters
Begin
Result:=False;
For by:=32 To 127 Do c[by]:=Char(by);
If IgnoreSpaces Then
Begin
p1:=PChar(StringReplace(S1,' ','',[rfReplaceAll]));
p2:=PChar(StringReplace(S2,' ','',[rfReplaceAll]));
end
Else
Begin
p1:=PChar(S1);
p2:=PChar(S2);
end;
l1:=Length(strpas(p1));
l2:=Length(strpas(p2));
If l1<>l2 Then Exit; //unequal Length.
//We only step into this code if l1=l2
i:=StrSpn(p1,PChar(@c[32]));
If i<>l1 Then Exit; //Illegal char
i:=StrSpn(p2,PChar(@c[32]));
If i<>l2 Then Exit; //Illegal char
SetLength(ps1,l1);
SetLength(ps2,l2);
For i:=0 To l1-1 Do
Begin
ps1[i]:=p1^;
Inc(p1);
ps2[i]:=p2^;
Inc(p2);
end;
QuickSort(ps1,Low(ps1),High(ps1));
QuickSort(ps2,Low(ps2),High(ps2));
Result:=CompareMem(@ps1[0],@ps2[0],Length(ps1));
End;
function IsAnagram_Zvoni2(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
Var
ls1,ls2:String;
F1,F2:Array[#32..#127] Of UInt32;
i:Integer;
Begin
Result:=False;
If IgnoreSpaces Then
Begin
ls1:=LowerCase(StringReplace(S1,' ','',[rfReplaceAll]));
ls2:=LowerCase(StringReplace(S2,' ','',[rfReplaceAll]));
end
Else
Begin
ls1:=LowerCase(S1);
ls2:=LowerCase(S2);
End;
If Length(ls1)<>Length(ls2) Then Exit;
//Both Strings have same Length from here
FillChar(f1,SizeOf(f1),0);
FillChar(f2,SizeOf(f2),0);
For i:=1 To Length(ls1) Do
Begin
Try
Inc(F1[ls1[i]]);
Except
On Exception Do
If ExceptionOnError Then
ERangeError.CreateFmt('Illegal character in s1, position %d(#%d)', [i, Ord(ls1[i])])
Else
Exit;
End;
Try
Inc(F2[ls2[i]]);
Except
On Exception Do
If ExceptionOnError Then
ERangeError.CreateFmt('Illegal character in s2, position %d(#%d)', [i, Ord(ls2[i])])
Else
Exit;
End;
End;
Result:=CompareByte(F1,F2,Length(F1))=0;
End;
function IsAnagram_ALLIGATOR(const S1, S2: String; IgnoreSpaces: Boolean = True; ExceptionOnError: Boolean = False): Boolean;
type
TFreq = array [32..127] of Int32;
const
FZero: TFreq = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0);
var
i: SizeInt;
F1: TFreq;
Ch: Byte;
begin
Result := False;
FillChar(F1, SizeOf(F1), 0);
i:=0;
while i<Length(S1) do
begin
inc(i);
Ch:=ord(S1[i]);
case Ch of
32..64, 91..122: Inc(F1[Ch]);
65..90: Inc(F1[Ch or $20]);
else
if ExceptionOnError then Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d',[i]);
end;
end;
i:=0;
while i<Length(S2) do
begin
inc(i);
Ch:=ord(S2[i]);
case Ch of
32..64, 91..122: Dec(F1[Ch]);
65..90: Dec(F1[Ch or $20]);
else
if ExceptionOnError then Raise ERangeError.CreateFmt('IsAnagram: illegal character in S1 at position %d',[i]);
end;
end;
if IgnoreSpaces then
begin
Result := CompareMem(@F1[Low(F1)+1], @FZero, SizeOf(TFreq)-SizeOf(TFreq[Low(TFreq)]));
end else
begin
Result := CompareMem(@F1, @FZero, SizeOf(TFreq));
end;
end;
function IsAnagram_silvercoder70(const S1, S2: String;
IgnoreSpaces: Boolean = True;
ExceptionOnError: Boolean = False): Boolean;
var
CharCount: array[Byte] of Integer;
i, j: Integer;
begin
Result := False;
FillChar(CharCount, SizeOf(CharCount), #0);
// update array based on S1...
for i := 1 to Length(S1) do
begin
if Ord(S1[i]) < 32 then
begin
if ExceptionOnError then
raise Exception.Create('Invalid character in S1: ' + S1[i]);
Exit;
end;
if (not IgnoreSpaces) or (S1[i] <> ' ') then
CharCount[Ord(Lowercase(S1[i]))] :=
CharCount[Ord(Lowercase(S1[i]))] + 1;
end;
// now checked elements in S2...
for j := 1 to Length(S2) do
begin
if Ord(S2[j]) < 32 then
begin
if ExceptionOnError then
raise Exception.Create('Invalid character in S2: ' + S2[j]);
Exit;
end;
// Convert to lowercase and decrement count
if (not IgnoreSpaces) or (S2[j] <> ' ') then
begin
if CharCount[Ord(Lowercase(S2[j]))] = 0 then
begin
//if ExceptionOnError then
//raise Exception.Create('Strings are not anagrams.');
Exit;
end;
CharCount[Ord(Lowercase(S2[j]))] :=
CharCount[Ord(Lowercase(S2[j]))] - 1;
end;
end;
for i := Low(Byte) to High(Byte) do
begin
if CharCount[i] > 0 then
begin
//if ExceptionOnError then
//raise Exception.Create('Strings are not anagrams.');
Exit;
end;
end;
Result := True;
end;
function IsAnagram_avk(const s1, s2: string; aIgnoreSpaces: Boolean = True; aExceptionOnError: Boolean = False): Boolean;
var
Counter: array[#32..#127] of Integer;
I: Integer;
c: AnsiChar;
begin
FillChar(Counter, SizeOf(Counter), 0);
for I := 1 to Length(s1) do begin
if DWord(Integer(s1[I])-Integer(32)) > DWord(96) then
if aExceptionOnError then
raise ERangeError.CreateFmt('Illegal character in s1, position %d(#%d)', [I, Ord(s1[I])])
else
exit(False);
Inc(Counter[s1[I]]);
end;
for I := 1 to Length(s2) do begin
if DWord(Integer(s2[I])-Integer(32)) > DWord(96) then
if aExceptionOnError then
raise ERangeError.CreateFmt('Illegal character in s2, position %d(#%d)', [I, Ord(s2[I])])
else
exit(False);
Dec(Counter[s2[I]]);
end;
for c := AnsiChar(32 + Ord(aIgnoreSpaces)) to #127 do
if Counter[c] <> 0 then exit(False);
Result := True;
end;
procedure main;
var
s, d: string;
procedure Test(const AFunctionName: string; const AFunc: IsAnagramFunc; const IgnoreSpaces, ExceptionOnError: Boolean);
const
ITERATIONS = 1000*1000*25;
IgnoreSpacesStr: array[boolean] of string = ('', ' IgnoreSpaces');
ExceptionOnErrorStr: array[boolean] of string = ('', ' ExceptionOnError');
var
i, c: integer;
u: ptruint;
begin
write(Concat(AFunctionName, IgnoreSpacesStr[IgnoreSpaces], ExceptionOnErrorStr[ExceptionOnError]):50);
c := 0;
u := GetTickCount64;
for i := 1 to ITERATIONS do if AFunc(s, d, IgnoreSpaces, ExceptionOnError) then c += 1;
write(' | ', (GetTickCount64-u):5, ' ms');
write(' | result ', c);
writeln;
end;
procedure TestAll;
begin
Test('Bart', @IsAnagram, true, true);
Test('Fibonacci', @IsAnagram_fibo, true, true);
Test('ASerge', @IsAnagramASerge, true, true);
Test('Zvoni', @IsAnagram_Zvoni, true, true);
Test('Zvoni (2)', @IsAnagram_Zvoni2, true, true);
Test('ALLIGATOR', @IsAnagram_ALLIGATOR, true, true);
Test('silvercoder70', @IsAnagram_silvercoder70, true, true);
Test('avk', @IsAnagram_avk, true, true);
Test('Bart', @IsAnagram, true, false);
Test('Fibonacci', @IsAnagram_fibo, true, false);
Test('ASerge', @IsAnagramASerge, true, false);
Test('Zvoni', @IsAnagram_Zvoni, true, false);
Test('Zvoni (2)', @IsAnagram_Zvoni2, true, false);
Test('ALLIGATOR', @IsAnagram_ALLIGATOR, true, false);
Test('silvercoder70', @IsAnagram_silvercoder70, true, false);
Test('avk', @IsAnagram_avk, true, false);
Test('Bart', @IsAnagram, false, false);
Test('Fibonacci', @IsAnagram_fibo, false, false);
Test('ASerge', @IsAnagramASerge, false, false);
Test('Zvoni', @IsAnagram_Zvoni, false, false);
Test('Zvoni (2)', @IsAnagram_Zvoni2, false, false);
Test('ALLIGATOR', @IsAnagram_ALLIGATOR, false, false);
Test('silvercoder70', @IsAnagram_silvercoder70, false, false);
Test('avk', @IsAnagram_avk, false, false);
end;
begin
//writeln('IsAnagram_fibo = ', IsAnagram_fibo('St a te @', 'tas t e `'));
//writeln('IsAnagram_fibo = ', IsAnagram_fibo('St a te !', 'tas t e `'));
//writeln('IsAnagram_fibo = ', IsAnagram_fibo('St a te ', 'tas t e '));
//writeln('IsAnagram_fibo = ', IsAnagram_fibo('state', 'taste'));
//readln;exit;
s := 's tate';
d := 't a s t e';
writeln('*** ROUND 1 ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
TestAll;
writeln;
s := 'night';
d := 'THING';
writeln('*** ROUND 2 ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
TestAll;
writeln;
s := 'Invalid';
d := 'Diff length';
writeln('*** ROUND 3: Invalid chars ***');
writeln('s1 = ', s);
writeln('s2 = ', d);
writeln;
TestAll;
readln;
end;
begin
main;
end.