Before attempting this problem, you should be comfortable with:
The most direct approach is to examine every substring of length k and check if it contains all unique characters. For each starting position, we scan k characters and track their frequencies. If any character appears more than once, we stop early and move to the next substring.
k > 26, return 0 immediately since there are only 26 lowercase letters.i from 0 to n - k:k characters.1, break out of the inner loop (duplicate found).class Solution:
def numKLenSubstrNoRepeats(self, s: str, k: int) -> int:
if k > 26:
return 0
answer = 0
n = len(s)
for i in range(n - k + 1):
# Initializing an empty frequency array
freq = [0] * 26
for j in range(i, i + k):
curr_char = ord(s[j]) - ord("a")
# Incrementing the frequency of current character
freq[curr_char] += 1
# If a repeated character is found, we stop the loop
if freq[curr_char] > 1:
break
else:
# If the substring does not have any repeated characters,
# we increment the answer
answer += 1
return answerWhere is the length of
s, is the given substring length, and is the number of unique characters allowed in the string. In this case, .
Instead of recomputing character frequencies for each substring from scratch, we can maintain a sliding window. As we move the window, we add the new character on the right and remove the character that falls off on the left. When a duplicate is detected, we shrink the window from the left until all characters are unique again.
k > 26, return 0 (impossible to have k unique characters).left = 0 and right = 0, and a frequency array.right < n:s[right] to the frequency array.s[right] exceeds 1, remove s[left] from the window and increment left.k, increment the answer, then shrink the window from the left by one to prepare for the next position.right forward.class Solution:
def numKLenSubstrNoRepeats(self, s: str, k: int) -> int:
# We can reuse the condition from the first approach
# as for k > 26, there can be no substrings with only unique characters
if k > 26:
return 0
answer = 0
n = len(s)
# Initializing the left and right pointers
left = right = 0
# Initializing an empty frequency array
freq = [0] * 26
# Function to obtain the index of a character according to the alphabet
def get_val(ch: str) -> int:
return ord(ch) - ord("a")
while right < n:
# Add the current character in the frequency array
freq[get_val(s[right])] += 1
# If the current character appears more than once in the frequency array
# keep contracting the window and removing characters from the
# frequency array till the frequency of the current character becomes 1.
while freq[get_val(s[right])] > 1:
freq[get_val(s[left])] -= 1
left += 1
# Check if the length of the current unique substring is equal to k
if right - left + 1 == k:
answer += 1
# Contract the window and remove the leftmost character from the
# frequency array
freq[get_val(s[left])] -= 1
left += 1
# Expand the window
right += 1
return answerWhere is the length of
sand is the number of unique characters allowed in the string. In this case, .
Since the string contains only lowercase English letters, it's impossible to have a substring of length greater than 26 with all unique characters. Forgetting to add an early return for k > 26 leads to unnecessary computation or incorrect results when the problem guarantees such substrings cannot exist.
In the sliding window approach, when a duplicate character is found, you must shrink the window from the left until the duplicate is removed. A common mistake is shrinking by exactly one position or shrinking until the window size is less than k. The correct approach is to shrink until the frequency of the newly added character becomes 1.
When iterating through possible starting positions for substrings of length k, the loop should run from 0 to n - k (inclusive). Using n - k + 1 as the upper bound or forgetting the inclusive boundary leads to either missing valid substrings or accessing indices out of bounds.