11package com .github .refhumbold .algolib .text ;
22
3- import java .util .Comparator ;
43import java .util .HashMap ;
54import java .util .List ;
65import java .util .Map ;
76import java .util .Objects ;
87import java .util .function .BiFunction ;
98import java .util .stream .IntStream ;
10- import com . github . refhumbold . algolib . tuples . ComparablePair ;
9+ import java . util . stream . Stream ;
1110import com .github .refhumbold .algolib .tuples .Pair ;
1211
13- /** Structure of base words map using Karp-Miller-Rosenberg algorithm. */
14- public final class BaseWordsMap
12+ /** Structure of basic factors map using Karp-Miller-Rosenberg algorithm. */
13+ public final class BasicFactorsMap
1514{
1615 private final String text ;
1716 private final Map <Pair <Integer , Integer >, Integer > factors = new HashMap <>();
1817
19- private BaseWordsMap (String text )
18+ private BasicFactorsMap (String text )
2019 {
2120 this .text = text ;
2221 create ();
@@ -27,21 +26,16 @@ public String getText()
2726 return text ;
2827 }
2928
30- public static BaseWordsMap build (String text )
29+ public static BasicFactorsMap build (String text )
3130 {
32- return new BaseWordsMap (text );
31+ return new BasicFactorsMap (text );
3332 }
3433
3534 @ Override
3635 public boolean equals (Object obj )
3736 {
38- if (this == obj )
39- return true ;
40-
41- if (!(obj instanceof BaseWordsMap other ))
42- return false ;
43-
44- return Objects .equals (text , other .text ) && Objects .equals (factors , other .factors );
37+ return this == obj || obj instanceof BasicFactorsMap other && Objects .equals (text ,
38+ other .text ) && Objects .equals (factors , other .factors );
4539 }
4640
4741 @ Override
@@ -68,57 +62,51 @@ public Pair<Integer, Integer> getCode(int startIndex)
6862 */
6963 public Pair <Integer , Integer > getCode (int startIndex , int endIndex )
7064 {
71- if (startIndex < 0 || startIndex >= text .length ())
72- throw new IndexOutOfBoundsException ("Index out of range %d" .formatted (startIndex ));
73-
74- if (endIndex < 0 || endIndex > text .length ())
75- throw new IndexOutOfBoundsException ("Index out of range %d" .formatted (endIndex ));
65+ if (startIndex < 0 || startIndex >= text .length () || endIndex < 0
66+ || endIndex > text .length ())
67+ throw new IndexOutOfBoundsException (
68+ "Range [%d, %d) out of bounds for text" .formatted (startIndex , endIndex ));
7669
7770 if (endIndex <= startIndex )
78- return Pair . of ( 0 , 0 );
71+ throw new IllegalArgumentException ( "Empty range of indices" );
7972
8073 Integer code = factors .get (Pair .of (startIndex , endIndex ));
8174
8275 if (code != null )
8376 return Pair .of (code , 0 );
8477
8578 int n = getMaxLength (endIndex - startIndex );
79+
8680 return Pair .of (factors .get (Pair .of (startIndex , startIndex + n )),
8781 factors .get (Pair .of (endIndex - n , endIndex )));
8882 }
8983
90- // Builds base words map using Karp-Miller-Rosenberg algorithm.
84+ // Builds basic factors map using Karp-Miller-Rosenberg algorithm.
9185 private void create ()
9286 {
9387 int codeValue = extend (1 , 0 ,
94- (i , length ) -> new int []{ text .charAt (i ), 1 + text .charAt (i ), i , i + length } );
88+ (i , length ) -> new ExtensionCode ( text .charAt (i ), 1 + text .charAt (i ), i ) );
9589
9690 for (int currentLength = 2 ; currentLength <= text .length (); currentLength *= 2 )
9791 codeValue = extend (currentLength , codeValue ,
98- (i , length ) -> new int []{ factors .get (Pair .of (i , i + length / 2 )),
99- factors .get (Pair .of (i + length / 2 , i + length )), i , i + length } );
92+ (i , length ) -> new ExtensionCode ( factors .get (Pair .of (i , i + length / 2 )),
93+ factors .get (Pair .of (i + length / 2 , i + length )), i ) );
10094 }
10195
10296 // Encodes substring of given length using already counted factors.
103- private int extend (int length , int codeValue , BiFunction <Integer , Integer , int [] > func )
97+ private int extend (int length , int codeValue , BiFunction <Integer , Integer , ExtensionCode > func )
10498 {
105- ComparablePair <Integer , Integer > previousCode = ComparablePair .of (0 , 0 );
106- List <int []> codes = IntStream .range (0 , text .length () - length + 1 )
107- .mapToObj (i -> func .apply (i , length ))
108- .sorted (new CodesComparator ())
109- .toList ();
99+ List <ExtensionCode > codes = Stream .concat (Stream .of (new ExtensionCode (0 , 0 , -1 )),
100+ IntStream .range (0 , text .length () - length + 1 )
101+ .mapToObj (i -> func .apply (i , length ))
102+ .sorted ()).toList ();
110103
111- for (int [] code : codes )
104+ for (int i = 1 ; i < codes . size (); ++ i )
112105 {
113- ComparablePair <Integer , Integer > codePair = ComparablePair .of (code [0 ], code [1 ]);
114-
115- if (!Objects .equals (previousCode , codePair ))
116- {
106+ if (!codes .get (i ).equals (codes .get (i - 1 )))
117107 ++codeValue ;
118- previousCode = codePair ;
119- }
120108
121- factors .put (Pair .of (code [ 2 ], code [ 3 ] ), codeValue );
109+ factors .put (Pair .of (codes . get ( i ). index , codes . get ( i ). index + length ), codeValue );
122110 }
123111
124112 return codeValue ;
@@ -138,17 +126,31 @@ private int getMaxLength(int n)
138126 return prev ;
139127 }
140128
141- private static class CodesComparator
142- implements Comparator < int [] >
129+ private record ExtensionCode ( int prefixCode , int suffixCode , int index )
130+ implements Comparable < ExtensionCode >
143131 {
144132 @ Override
145- public int compare (int [] a1 , int [] a2 )
133+ public boolean equals (Object obj )
134+ {
135+ return this == obj
136+ || obj instanceof ExtensionCode other && prefixCode == other .prefixCode
137+ && suffixCode == other .suffixCode ;
138+ }
139+
140+ @ Override
141+ public int hashCode ()
146142 {
147- return IntStream .range (0 , Math .min (a1 .length , a2 .length ))
148- .map (i -> Integer .compare (a1 [i ], a2 [i ]))
149- .filter (compareInts -> compareInts != 0 )
150- .findFirst ()
151- .orElse (0 );
143+ return Objects .hash (prefixCode , suffixCode );
144+ }
145+
146+ @ Override
147+ public int compareTo (ExtensionCode code )
148+ {
149+ int comparePrefixCodes = Integer .compare (prefixCode , code .prefixCode );
150+
151+ return comparePrefixCodes != 0
152+ ? comparePrefixCodes
153+ : Integer .compare (suffixCode , code .suffixCode );
152154 }
153155 }
154156}
0 commit comments