Skip to content

Commit e43ec38

Browse files
authored
Add Duval's Lyndon factorization algorithm (#7639)
* Add Duval Lyndon factorization * Test Lyndon factorization * Handle null input explicitly in Lyndon factorization * Align null-input test with explicit validation
1 parent 5eb9d66 commit e43ec38

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.thealgorithms.strings;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
/**
7+
* Factorizes a string into its Lyndon words using Duval's algorithm.
8+
*
9+
* <p>Duval's algorithm runs in O(n) time and uses O(1) auxiliary working
10+
* space, excluding the returned list and its strings.
11+
*
12+
* <p>Reference:
13+
* https://cp-algorithms.com/string/lyndon_factorization.html
14+
*/
15+
public final class LyndonFactorization {
16+
private LyndonFactorization() {
17+
}
18+
19+
/**
20+
* Returns the standard Lyndon factorization of {@code text}.
21+
*
22+
* <p>The returned factors are in non-increasing lexicographic order
23+
* according to Java's UTF-16 code-unit ordering.
24+
*
25+
* @param text input string to factorize
26+
* @return Lyndon factors in non-increasing lexicographic order
27+
* @throws IllegalArgumentException if {@code text} is null
28+
*/
29+
public static List<String> factorize(String text) {
30+
if (text == null) {
31+
throw new IllegalArgumentException("text must not be null");
32+
}
33+
34+
List<String> factors = new ArrayList<>();
35+
int n = text.length();
36+
int start = 0;
37+
38+
while (start < n) {
39+
int candidate = start + 1;
40+
int match = start;
41+
42+
while (candidate < n && text.charAt(match) <= text.charAt(candidate)) {
43+
if (text.charAt(match) < text.charAt(candidate)) {
44+
match = start;
45+
} else {
46+
match++;
47+
}
48+
candidate++;
49+
}
50+
51+
int length = candidate - match;
52+
53+
while (start <= match) {
54+
factors.add(text.substring(start, start + length));
55+
start += length;
56+
}
57+
}
58+
59+
return factors;
60+
}
61+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.thealgorithms.strings;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.util.List;
7+
import org.junit.jupiter.api.Test;
8+
9+
class LyndonFactorizationTest {
10+
11+
@Test
12+
void factorizesBanana() {
13+
assertEquals(List.of("b", "an", "an", "a"), LyndonFactorization.factorize("banana"));
14+
}
15+
16+
@Test
17+
void factorizesRepeatedPattern() {
18+
assertEquals(List.of("ab", "ab", "ab"), LyndonFactorization.factorize("ababab"));
19+
}
20+
21+
@Test
22+
void factorizesSingleCharacter() {
23+
assertEquals(List.of("a"), LyndonFactorization.factorize("a"));
24+
}
25+
26+
@Test
27+
void factorizesEmptyString() {
28+
assertEquals(List.of(), LyndonFactorization.factorize(""));
29+
}
30+
31+
@Test
32+
void rejectsNullInput() {
33+
assertThrows(IllegalArgumentException.class, () -> LyndonFactorization.factorize(null));
34+
}
35+
}

0 commit comments

Comments
 (0)