From 28b09b8c1230ace9ebe8f0f9d3a23917800319de Mon Sep 17 00:00:00 2001 From: ShoaibAbbasDev Date: Fri, 23 Oct 2026 13:02:05 +0500 Subject: [PATCH 1/2] Add Lyndon factorization (Duval's algorithm), --- strings/lyndon_factorization.py | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 strings/lyndon_factorization.py diff --git a/strings/lyndon_factorization.py b/strings/lyndon_factorization.py new file mode 100644 index 000000000000..e6647b28d469 --- /dev/null +++ b/strings/lyndon_factorization.py @@ -0,0 +1,66 @@ +""" +https://en.wikipedia.org/wiki/Lyndon_word +https://cp-algorithms.com/string/lyndon_factorization.html + +Lyndon factorization (Duval's algorithm) + +A Lyndon word is a non-empty string that is strictly smaller than all of its +proper rotations. Every string can be written uniquely as a concatenation of +Lyndon words w1 >= w2 >= ... >= wk. Duval's algorithm finds this factorization. + +Time Complexity: O(n) - where n is the length of the string +Space Complexity: O(1) extra, apart from the output +""" + + +def lyndon_factorization(input_str: str) -> list[str]: + """ + Return the Lyndon factorization of the given string. + + >>> lyndon_factorization("abab") + ['ab', 'ab'] + >>> lyndon_factorization("banana") + ['b', 'an', 'an', 'a'] + >>> lyndon_factorization("aaa") + ['a', 'a', 'a'] + >>> lyndon_factorization("abc") + ['abc'] + >>> lyndon_factorization("cba") + ['c', 'b', 'a'] + >>> lyndon_factorization("a") + ['a'] + >>> lyndon_factorization("") + [] + >>> lyndon_factorization(123) + Traceback (most recent call last): + ... + TypeError: object of type 'int' has no len() + """ + length = len(input_str) + factors: list[str] = [] + start = 0 + + while start < length: + # `compare` scans ahead while the text keeps repeating the current word + compare = start + 1 + candidate = start + while compare < length and input_str[candidate] <= input_str[compare]: + if input_str[candidate] < input_str[compare]: + candidate = start + else: + candidate += 1 + compare += 1 + + # The repeated Lyndon word has length (compare - candidate) + word_length = compare - candidate + while start <= candidate: + factors.append(input_str[start : start + word_length]) + start += word_length + + return factors + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 4b7ff0ee87103a318752f5117feb4a1a304c6ae3 Mon Sep 17 00:00:00 2001 From: ShoaibAbbasDev Date: Fri, 23 Oct 2026 08:09:25 +0000 Subject: [PATCH 2/2] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 52b15110f236..5dbce818c8a9 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1615,6 +1615,7 @@ * [Levenshtein Distance](strings/levenshtein_distance.py) * [Longest Word In Sentence](strings/longest_word_in_sentence.py) * [Lower](strings/lower.py) + * [Lyndon Factorization](strings/lyndon_factorization.py) * [Manacher](strings/manacher.py) * [Min Cost String Conversion](strings/min_cost_string_conversion.py) * [Min Window Substring](strings/min_window_substring.py)