Skip to content

Commit a92accd

Browse files
authored
Merge pull request #105 from BrianLusina/feat/longest-self-contained-substring
feat(strings): longest self contained substring
2 parents e43536e + b80c428 commit a92accd

File tree

41 files changed

+374
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+374
-1
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions

pystrings/length_of_longest_substring/README.md

Lines changed: 1 addition & 1 deletion
Lines changed: 112 additions & 0 deletions
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
def longest_self_contained_substring(s: str) -> int:
2+
"""
3+
Finds the longest self-contained substring in a given string.
4+
5+
A self-contained substring is one where each character only appears within the substring itself.
6+
The function returns the length of the longest self-contained substring, and -1 if no such substring exists.
7+
8+
Parameters:
9+
s (str): The input string.
10+
11+
Returns:
12+
int: The length of the longest self-contained substring, or -1 if no such substring exists.
13+
14+
Examples:
15+
>>> longest_self_contained_substring("xyyx")
16+
2
17+
>>> longest_self_contained_substring("xyxy")
18+
-1
19+
>>> longest_self_contained_substring("abacd")
20+
4
21+
22+
Note:
23+
This implementation uses a brute-force approach with O(n³) time complexity.
24+
For better performance, consider using max_substring_length() which runs in O(n).
25+
"""
26+
n = len(s)
27+
28+
# First, find the first and last occurrence of each character
29+
# This helps us quickly check if a character appears outside a range
30+
first_occurrence = {}
31+
last_occurrence = {}
32+
33+
for i, char in enumerate(s):
34+
if char not in first_occurrence:
35+
first_occurrence[char] = i
36+
last_occurrence[char] = i
37+
38+
max_length = -1
39+
40+
# Try all possible substrings (excluding the entire string)
41+
for start in range(n):
42+
for end in range(start, n):
43+
# Skip the entire string
44+
if start == 0 and end == n - 1:
45+
continue
46+
47+
# Check if this substring is self-contained
48+
substring = s[start:end + 1]
49+
is_self_contained = True
50+
51+
# For each character in the substring, verify it doesn't appear outside
52+
for char in set(substring):
53+
# If the character's first occurrence is before our start
54+
# or last occurrence is after our end, it appears outside
55+
if first_occurrence[char] < start or last_occurrence[char] > end:
56+
is_self_contained = False
57+
break
58+
59+
# If self-contained, update our maximum
60+
if is_self_contained:
61+
max_length = max(max_length, end - start + 1)
62+
63+
return max_length
64+
65+
66+
def max_substring_length(s: str) -> int:
67+
"""
68+
Finds the length of the longest substring of s that is self-contained.
69+
70+
A self-contained substring is one in which all characters only appear within the substring.
71+
72+
The function uses an optimized window expansion approach. For each unique character as a starting point,
73+
it defines an initial window from the character's first to last occurrence. The window is expanded to include
74+
all occurrences of characters within it, and is invalidated if any character's first occurrence lies before
75+
the window start.
76+
77+
Parameters:
78+
s (str): The string to find the longest self-contained substring of
79+
80+
Returns:
81+
int: The length of the longest self-contained substring of s
82+
83+
Examples:
84+
>>> max_substring_length("xyyx")
85+
2
86+
>>> max_substring_length("xyxy")
87+
-1
88+
>>> max_substring_length("abacd")
89+
4
90+
91+
Note:
92+
Time complexity: O(n), Space complexity: O(1) for fixed character set size.
93+
"""
94+
first = {}
95+
last = {}
96+
for i, c in enumerate(s):
97+
if c not in first:
98+
first[c] = i
99+
last[c] = i
100+
101+
max_len = -1
102+
103+
for c1 in first:
104+
start = first[c1]
105+
end = last[c1]
106+
j = start
107+
108+
while j < len(s):
109+
c2 = s[j]
110+
if first[c2] < start:
111+
break
112+
end = max(end, last[c2])
113+
if end == j and end - start + 1 != len(s):
114+
max_len = max(max_len, end - start + 1)
115+
j += 1
116+
117+
return max_len
77.3 KB
75.1 KB
85.6 KB
78.5 KB
75.2 KB
74.7 KB

0 commit comments

Comments
 (0)