Skip to content

Commit 1f84e8d

Browse files
committed
feat(strings, self-contained-substring): longest self contained substring
1 parent e43536e commit 1f84e8d

File tree

40 files changed

+352
-1
lines changed

40 files changed

+352
-1
lines changed

pystrings/length_of_longest_substring/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Longest Substring Without Repeating Characters
1+
# Longest Substring Without Repeating Characters
22

33
Given a string s, find the length of the longest substring without repeating characters.
44

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Find Longest Self-Contained Substring
2+
3+
You are given a string, s, consisting of lowercase English letters. Your task is to find the length of the longest
4+
self-contained substring of s.
5+
6+
A substring t of s is called self-contained if:
7+
- t is not equal to the entire string s.
8+
- Every character in t does not appear anywhere else in s (outside of t).
9+
10+
In other words, all characters in t are completely unique to that substring within the string s.
11+
Return the length of the longest self-contained substring. If no such substring exists, return -1.
12+
13+
Constraints:
14+
15+
- 2 ≤ s.length ≤ 1000
16+
- s consists only of lowercase English letters.
17+
18+
## Examples
19+
20+
![Example 1](./images/longest_self_contained_substring_example_1.png)
21+
![Example 2](./images/longest_self_contained_substring_example_2.png)
22+
![Example 3](./images/longest_self_contained_substring_example_3.png)
23+
![Example 4](./images/longest_self_contained_substring_example_4.png)
24+
![Example 5](./images/longest_self_contained_substring_example_5.png)
25+
![Example 6](./images/longest_self_contained_substring_example_6.png)
26+
![Example 7](./images/longest_self_contained_substring_example_7.png)
27+
![Example 8](./images/longest_self_contained_substring_example_8.png)
28+
29+
---
30+
31+
## Solution
32+
33+
We iterate through the string once to record where each character first appears and where it last appears. Two separate
34+
hash maps store each character’s first and last occurrence indices. Each unique character serves as a potential starting
35+
point, defining an initial window from its first to last occurrence. The window is adjusted based on the following
36+
conditions:
37+
38+
- As we iterate within this window, if we encounter a character whose last occurrence is further to the right than the
39+
current window’s end, we expand the window to include it. This ensures that the substring remains self-contained,
40+
meaning all occurrences of each character within it are included. The process continues until no more characters
41+
extend the window’s boundary.
42+
43+
- As we expand the window, every character inside it must have its first occurrence within the current window’s start
44+
boundary. If we encounter a character whose first occurrence index is before the current window’s starting position,
45+
it means that an earlier part of the string contains an instance of that character, violating the self-contained
46+
property. When this happens, the current substring is invalid, and we discard it from consideration.
47+
48+
The maximum valid window length is tracked and updated accordingly, ensuring the longest valid substring is returned.
49+
50+
The steps of the algorithm are as follows:
51+
52+
1. Create two hashmaps, first, and last, to store the first and last occurrence index of each character in the string.
53+
2. Iterate through the string once to populate these hash maps with each character’s first and last occurrence.
54+
3. Initialize max_len = -1 to keep track of the maximum length of a valid self-contained substring found.
55+
4. For each unique character c1 (processed once), start from its first occurrence index:
56+
57+
- Initialize the start and end of the window by setting the starting point to the character’s first occurrence and the
58+
ending point to its last occurrence in the string.
59+
60+
- Iterate through the string from the starting position start, extending the endpoint end whenever a character’s last
61+
occurrence is beyond the current endpoint end.
62+
63+
- If a character c2 inside the window has its first occurrence before the window's start, the window is invalid.
64+
65+
5. Validate the substring:
66+
67+
- When the current index j reaches the end, check if the window is valid and its length is less than the total string
68+
length.
69+
70+
- If the window is valid, update max_len with the maximum of its current value and the window’s length (end - start + 1).
71+
72+
6. After checking all potential starting characters, return the maximum valid length found. If no valid substring exists,
73+
return -1.
74+
75+
Let’s look at the illustration below to better understand the solution.
76+
77+
![Solution 1](./images/solution/longest_self_contained_substring_solution_1.png)
78+
![Solution 2](./images/solution/longest_self_contained_substring_solution_2.png)
79+
![Solution 3](./images/solution/longest_self_contained_substring_solution_3.png)
80+
![Solution 4](./images/solution/longest_self_contained_substring_solution_4.png)
81+
![Solution 5](./images/solution/longest_self_contained_substring_solution_5.png)
82+
![Solution 6](./images/solution/longest_self_contained_substring_solution_6.png)
83+
![Solution 7](./images/solution/longest_self_contained_substring_solution_7.png)
84+
![Solution 8](./images/solution/longest_self_contained_substring_solution_8.png)
85+
![Solution 9](./images/solution/longest_self_contained_substring_solution_9.png)
86+
![Solution 10](./images/solution/longest_self_contained_substring_solution_10.png)
87+
![Solution 11](./images/solution/longest_self_contained_substring_solution_11.png)
88+
![Solution 12](./images/solution/longest_self_contained_substring_solution_12.png)
89+
![Solution 13](./images/solution/longest_self_contained_substring_solution_13.png)
90+
![Solution 14](./images/solution/longest_self_contained_substring_solution_14.png)
91+
![Solution 15](./images/solution/longest_self_contained_substring_solution_15.png)
92+
![Solution 16](./images/solution/longest_self_contained_substring_solution_16.png)
93+
![Solution 17](./images/solution/longest_self_contained_substring_solution_17.png)
94+
![Solution 18](./images/solution/longest_self_contained_substring_solution_18.png)
95+
![Solution 19](./images/solution/longest_self_contained_substring_solution_19.png)
96+
![Solution 20](./images/solution/longest_self_contained_substring_solution_20.png)
97+
![Solution 21](./images/solution/longest_self_contained_substring_solution_21.png)
98+
![Solution 22](./images/solution/longest_self_contained_substring_solution_22.png)
99+
![Solution 23](./images/solution/longest_self_contained_substring_solution_23.png)
100+
![Solution 24](./images/solution/longest_self_contained_substring_solution_24.png)
101+
![Solution 25](./images/solution/longest_self_contained_substring_solution_25.png)
102+
![Solution 26](./images/solution/longest_self_contained_substring_solution_26.png)
103+
![Solution 27](./images/solution/longest_self_contained_substring_solution_27.png)
104+
![Solution 28](./images/solution/longest_self_contained_substring_solution_28.png)
105+
106+
### Time Complexity
107+
108+
The time complexity of the above solution is O(n), where n is the number of characters in the string.
109+
110+
### Space Complexity
111+
112+
The space complexity of the above solution is O(1) because of the fixed character set size, 26 lowercase English letters.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
"""
15+
n = len(s)
16+
17+
# First, find the first and last occurrence of each character
18+
# This helps us quickly check if a character appears outside a range
19+
first_occurrence = {}
20+
last_occurrence = {}
21+
22+
for i, char in enumerate(s):
23+
if char not in first_occurrence:
24+
first_occurrence[char] = i
25+
last_occurrence[char] = i
26+
27+
max_length = -1
28+
29+
# Try all possible substrings (excluding the entire string)
30+
for start in range(n):
31+
for end in range(start, n):
32+
# Skip the entire string
33+
if start == 0 and end == n - 1:
34+
continue
35+
36+
# Check if this substring is self-contained
37+
substring = s[start:end + 1]
38+
is_self_contained = True
39+
40+
# For each character in the substring, verify it doesn't appear outside
41+
for char in set(substring):
42+
# If the character's first occurrence is before our start
43+
# or last occurrence is after our end, it appears outside
44+
if first_occurrence[char] < start or last_occurrence[char] > end:
45+
is_self_contained = False
46+
break
47+
48+
# If self-contained, update our maximum
49+
if is_self_contained:
50+
max_length = max(max_length, end - start + 1)
51+
52+
return max_length
53+
54+
55+
def max_substring_length(s):
56+
"""
57+
Finds the length of the longest substring of s that is self-contained.
58+
59+
A self-contained substring is one in which all characters only appear within the substring.
60+
61+
The function works by iterating over all possible substrings of s and checking if each one is self-contained.
62+
63+
It does this by keeping track of the first and last occurrence of each character in s. It then checks if each
64+
character in a substring appears outside of the substring's range. If it does, the substring is not self-contained.
65+
66+
Finally, it returns the length of the longest self-contained substring it found.
67+
68+
Parameters:
69+
s (str): The string to find the longest self-contained substring of
70+
71+
Returns:
72+
int: The length of the longest self-contained substring of s
73+
"""
74+
first = {}
75+
last = {}
76+
for i, c in enumerate(s):
77+
if c not in first:
78+
first[c] = i
79+
last[c] = i
80+
81+
max_len = -1
82+
83+
for c1 in first:
84+
start = first[c1]
85+
end = last[c1]
86+
j = start
87+
88+
while j < len(s):
89+
c2 = s[j]
90+
if first[c2] < start:
91+
break
92+
end = max(end, last[c2])
93+
if end == j and end - start + 1 != len(s):
94+
max_len = max(max_len, end - start + 1)
95+
j += 1
96+
97+
return max_len
77.3 KB
Loading
75.1 KB
Loading
85.6 KB
Loading
78.5 KB
Loading
75.2 KB
Loading
74.7 KB
Loading
81.3 KB
Loading

0 commit comments

Comments
 (0)