Skip to content

Commit e9ee293

Browse files
committed
feat: add longest repeating subsequence algorithm
Add a dynamic programming implementation of the Longest Repeating Subsequence (LRS) algorithm. This finds the longest subsequence that occurs at least twice in a string, using a variation of LCS where the string is compared with itself but characters at the same index cannot be reused. Includes comprehensive doctests and type hints.
1 parent e3b01ec commit e9ee293

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
Longest Repeating Subsequence (LRS)
3+
4+
Given a string, find the length of the longest repeating subsequence, i.e., the
5+
longest subsequence that occurs at least twice. The two occurrences must use
6+
characters at different positions in the original string.
7+
8+
This is a variation of the Longest Common Subsequence (LCS) problem where we find
9+
the LCS of the string with itself, with the constraint that characters at the same
10+
index position cannot both be used.
11+
12+
Reference: https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
13+
"""
14+
15+
16+
def longest_repeating_subsequence(string: str) -> int:
17+
"""
18+
Find the length of the longest repeating subsequence in the given string.
19+
20+
A repeating subsequence is a subsequence that appears at least twice in the
21+
string, where characters at the same index are not counted as part of both
22+
subsequences simultaneously.
23+
24+
Uses dynamic programming with time complexity O(n^2) and space complexity O(n^2),
25+
where n is the length of the input string.
26+
27+
Parameters
28+
----------
29+
string : str
30+
The input string to search for repeating subsequences.
31+
32+
Returns
33+
-------
34+
int
35+
The length of the longest repeating subsequence.
36+
37+
Examples
38+
--------
39+
>>> longest_repeating_subsequence("aabb")
40+
2
41+
>>> longest_repeating_subsequence("aab")
42+
1
43+
>>> longest_repeating_subsequence("axxxy")
44+
2
45+
>>> longest_repeating_subsequence("abcabc")
46+
3
47+
>>> longest_repeating_subsequence("")
48+
0
49+
>>> longest_repeating_subsequence("a")
50+
0
51+
>>> longest_repeating_subsequence("abcdef")
52+
0
53+
>>> longest_repeating_subsequence("aaa")
54+
2
55+
>>> longest_repeating_subsequence("aaaa")
56+
3
57+
>>> longest_repeating_subsequence(12345)
58+
Traceback (most recent call last):
59+
...
60+
TypeError: Input must be a string, got int
61+
"""
62+
if not isinstance(string, str):
63+
msg = f"Input must be a string, got {type(string).__name__}"
64+
raise TypeError(msg)
65+
66+
n = len(string)
67+
if n == 0:
68+
return 0
69+
70+
# dp[i][j] stores the length of the longest repeating subsequence
71+
# considering string[0..i-1] and string[0..j-1]
72+
dp = [[0] * (n + 1) for _ in range(n + 1)]
73+
74+
for i in range(1, n + 1):
75+
for j in range(1, n + 1):
76+
# Characters match and are at different positions
77+
if string[i - 1] == string[j - 1] and i != j:
78+
dp[i][j] = dp[i - 1][j - 1] + 1
79+
else:
80+
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
81+
82+
return dp[n][n]
83+
84+
85+
if __name__ == "__main__":
86+
import doctest
87+
88+
doctest.testmod()

0 commit comments

Comments
 (0)