From ddee83c67a4109e21eea10ea753d2c5d4839496b Mon Sep 17 00:00:00 2001 From: Vedant Hemraj Kapgate Date: Thu, 8 Oct 2026 17:24:40 +0530 Subject: [PATCH 1/4] Add Duval Lyndon factorization --- .../strings/LyndonFactorization.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/main/java/com/thealgorithms/strings/LyndonFactorization.java diff --git a/src/main/java/com/thealgorithms/strings/LyndonFactorization.java b/src/main/java/com/thealgorithms/strings/LyndonFactorization.java new file mode 100644 index 000000000000..7f8806c321b2 --- /dev/null +++ b/src/main/java/com/thealgorithms/strings/LyndonFactorization.java @@ -0,0 +1,60 @@ +package com.thealgorithms.strings; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Factorizes a string into its Lyndon words using Duval's algorithm. + * + *

Duval's algorithm runs in O(n) time and uses O(1) auxiliary working + * space, excluding the returned list and its strings. + * + *

Reference: + * https://cp-algorithms.com/string/lyndon_factorization.html + */ +public final class LyndonFactorization { + private LyndonFactorization() { + } + + /** + * Returns the standard Lyndon factorization of {@code text}. + * + *

The returned factors are in non-increasing lexicographic order + * according to Java's UTF-16 code-unit ordering. + * + * @param text input string to factorize + * @return Lyndon factors in non-increasing lexicographic order + * @throws NullPointerException if {@code text} is null + */ + public static List factorize(String text) { + Objects.requireNonNull(text, "text must not be null"); + + List factors = new ArrayList<>(); + int n = text.length(); + int start = 0; + + while (start < n) { + int candidate = start + 1; + int match = start; + + while (candidate < n && text.charAt(match) <= text.charAt(candidate)) { + if (text.charAt(match) < text.charAt(candidate)) { + match = start; + } else { + match++; + } + candidate++; + } + + int length = candidate - match; + + while (start <= match) { + factors.add(text.substring(start, start + length)); + start += length; + } + } + + return factors; + } +} From 4ae5410d7f9ad027a132e487926f358a088d199d Mon Sep 17 00:00:00 2001 From: Vedant Hemraj Kapgate Date: Thu, 8 Oct 2026 17:24:42 +0530 Subject: [PATCH 2/4] Test Lyndon factorization --- .../strings/LyndonFactorizationTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java diff --git a/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java b/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java new file mode 100644 index 000000000000..90967399d5a6 --- /dev/null +++ b/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.strings; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class LyndonFactorizationTest { + + @Test + void factorizesBanana() { + assertEquals(List.of("b", "an", "an", "a"), LyndonFactorization.factorize("banana")); + } + + @Test + void factorizesRepeatedPattern() { + assertEquals(List.of("ab", "ab", "ab"), LyndonFactorization.factorize("ababab")); + } + + @Test + void factorizesSingleCharacter() { + assertEquals(List.of("a"), LyndonFactorization.factorize("a")); + } + + @Test + void factorizesEmptyString() { + assertEquals(List.of(), LyndonFactorization.factorize("")); + } + + @Test + void rejectsNullInput() { + assertThrows(NullPointerException.class, () -> LyndonFactorization.factorize(null)); + } +} From 5626d97f026960bd08403f88ac2eb94ad8d49e10 Mon Sep 17 00:00:00 2001 From: Vedant Hemraj Kapgate Date: Fri, 9 Oct 2026 19:43:07 +0530 Subject: [PATCH 3/4] Handle null input explicitly in Lyndon factorization --- .../com/thealgorithms/strings/LyndonFactorization.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/strings/LyndonFactorization.java b/src/main/java/com/thealgorithms/strings/LyndonFactorization.java index 7f8806c321b2..3eb13b00d0fb 100644 --- a/src/main/java/com/thealgorithms/strings/LyndonFactorization.java +++ b/src/main/java/com/thealgorithms/strings/LyndonFactorization.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Factorizes a string into its Lyndon words using Duval's algorithm. @@ -25,10 +24,12 @@ private LyndonFactorization() { * * @param text input string to factorize * @return Lyndon factors in non-increasing lexicographic order - * @throws NullPointerException if {@code text} is null + * @throws IllegalArgumentException if {@code text} is null */ public static List factorize(String text) { - Objects.requireNonNull(text, "text must not be null"); + if (text == null) { + throw new IllegalArgumentException("text must not be null"); + } List factors = new ArrayList<>(); int n = text.length(); From e5dc516b34b1990a5bf2ae77ce2b7051ca43b0e4 Mon Sep 17 00:00:00 2001 From: Vedant Hemraj Kapgate Date: Fri, 9 Oct 2026 19:43:13 +0530 Subject: [PATCH 4/4] Align null-input test with explicit validation --- .../java/com/thealgorithms/strings/LyndonFactorizationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java b/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java index 90967399d5a6..4ecb86e2a99c 100644 --- a/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java +++ b/src/test/java/com/thealgorithms/strings/LyndonFactorizationTest.java @@ -30,6 +30,6 @@ void factorizesEmptyString() { @Test void rejectsNullInput() { - assertThrows(NullPointerException.class, () -> LyndonFactorization.factorize(null)); + assertThrows(IllegalArgumentException.class, () -> LyndonFactorization.factorize(null)); } }