From 6f748026e5ebaeab3775351725502b2cbec1ef61 Mon Sep 17 00:00:00 2001 From: Rupesh <206439536+Rupeshhsharma@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:46:28 +0530 Subject: [PATCH 1/2] Add Longest Repeated Substring algorithm --- strings/longest_repeated_substring.py | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 strings/longest_repeated_substring.py diff --git a/strings/longest_repeated_substring.py b/strings/longest_repeated_substring.py new file mode 100644 index 000000000000..304acd39b776 --- /dev/null +++ b/strings/longest_repeated_substring.py @@ -0,0 +1,73 @@ +def longest_repeated_substring(s: str) -> str: + """ + Longest repeated (overlapping allowed) substring in O(n) using a suffix automaton. + Returns empty string if no repetition. + """ + n = len(s) + if n <= 1: + return "" + + class State: + __slots__ = ("next", "link", "length", "first_pos", "occ") + def __init__(self): + self.next = {} + self.link = -1 + self.length = 0 + self.first_pos = -1 + self.occ = 0 + + st = [State()] + last = 0 + + def sa_extend(c, pos): + nonlocal last + cur = len(st) + st.append(State()) + st[cur].length = st[last].length + 1 + st[cur].first_pos = pos + st[cur].occ = 1 + p = last + while p >= 0 and c not in st[p].next: + st[p].next[c] = cur + p = st[p].link + if p == -1: + st[cur].link = 0 + else: + q = st[p].next[c] + if st[p].length + 1 == st[q].length: + st[cur].link = q + else: + clone = len(st) + st.append(State()) + st[clone].next = st[q].next.copy() + st[clone].length = st[p].length + 1 + st[clone].link = st[q].link + st[clone].first_pos = st[q].first_pos + while p >= 0 and st[p].next.get(c) == q: + st[p].next[c] = clone + p = st[p].link + st[q].link = clone + st[cur].link = clone + last = cur + + for i, ch in enumerate(s): + sa_extend(ch, i) + + order = sorted(range(len(st)), key=lambda i: st[i].length, reverse=True) + for v in order: + if st[v].link != -1: + st[st[v].link].occ += st[v].occ + + best_len = 0 + best_end = -1 + for state in st: + if state.occ >= 2 and state.length > best_len: + best_len = state.length + best_end = state.first_pos + if best_len == 0: + return "" + return s[best_end - best_len + 1: best_end + 1] + + +if __name__ == "__main__": + print(longest_repeated_substring("banana")) \ No newline at end of file From 95e91292c389b3d1a2481c90eba44339e709efe0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:27:59 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- strings/longest_repeated_substring.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/strings/longest_repeated_substring.py b/strings/longest_repeated_substring.py index 304acd39b776..2742865f0536 100644 --- a/strings/longest_repeated_substring.py +++ b/strings/longest_repeated_substring.py @@ -9,12 +9,13 @@ def longest_repeated_substring(s: str) -> str: class State: __slots__ = ("next", "link", "length", "first_pos", "occ") + def __init__(self): self.next = {} self.link = -1 self.length = 0 self.first_pos = -1 - self.occ = 0 + self.occ = 0 st = [State()] last = 0 @@ -66,8 +67,8 @@ def sa_extend(c, pos): best_end = state.first_pos if best_len == 0: return "" - return s[best_end - best_len + 1: best_end + 1] + return s[best_end - best_len + 1 : best_end + 1] if __name__ == "__main__": - print(longest_repeated_substring("banana")) \ No newline at end of file + print(longest_repeated_substring("banana"))