3456. Find Special Substring of Length K
Problem Description
You are given a string s
and an integer k
. Your task is to determine if there exists a substring in s
that meets all of the following conditions:
- The substring has exactly length
k
- The substring consists of only one repeated character (like
"aaa"
or"bbb"
) - If there's a character immediately before this substring, it must be different from the character in the substring
- If there's a character immediately after this substring, it must be different from the character in the substring
In simpler terms, you need to find a run of exactly k
identical consecutive characters that is either:
- At the beginning of the string (no character before it) or preceded by a different character
- At the end of the string (no character after it) or followed by a different character
For example:
- In string
"aaabbb"
withk = 3
, the substring"aaa"
satisfies all conditions (exactly 3 'a's, nothing before, 'b' after which is different) - In string
"xaaay"
withk = 3
, the substring"aaa"
satisfies all conditions ('x' before which is different, 'y' after which is different) - In string
"aaaaa"
withk = 3
, there's no valid substring because any 3 consecutive 'a's would have another 'a' either before or after it
Return true
if such a substring exists, otherwise return false
.
Intuition
The key insight is that we're looking for segments of consecutive identical characters that have exactly length k
. When we read the problem conditions carefully, we realize that a valid substring must be "isolated" - it can't be part of a longer sequence of the same character.
Think about it this way: if we have a string like "aaabbbcccc"
, the consecutive segments are:
"aaa"
(3 a's)"bbb"
(3 b's)"cccc"
(4 c's)
For each segment, if its length equals k
, then it automatically satisfies all the conditions:
- It consists of only one repeated character ✓
- Any character before it (if exists) must be different, because otherwise it would be part of the same segment ✓
- Any character after it (if exists) must be different, for the same reason ✓
This leads us to a natural approach: instead of checking every possible substring of length k
and verifying all conditions, we can simply identify all maximal segments of identical consecutive characters and check if any of them has length exactly k
.
The two-pointer technique fits perfectly here. We use pointer l
to mark the start of a segment, and pointer r
to find where the segment ends (when we encounter a different character). The length of each segment is r - l
. If this equals k
, we've found our answer.
This approach is efficient because we only traverse the string once, and for each position, we immediately know if it's part of a valid segment or not.
Solution Approach
The solution uses a two-pointer technique to identify and measure consecutive segments of identical characters.
Algorithm Steps:
-
Initialize two pointers:
l
(left) starting at position 0, andn
as the length of the string. -
While
l
is within bounds (l < n
):- Set
r
(right pointer) to start at the same position asl
- Move
r
to the right as long ass[r] == s[l]
(finding all consecutive identical characters) - When
r
stops, we have a complete segment from indexl
tor-1
- Check if the segment length
r - l
equalsk
:- If yes, return
true
immediately (we found a valid substring) - If no, continue searching
- If yes, return
- Move
l
to positionr
(skip the entire processed segment)
- Set
-
If we've checked all segments and none has length
k
, returnfalse
Why this works:
- When we find a segment where
r - l == k
, this segment automatically satisfies all conditions:- It consists of only one character (we only moved
r
while characters matched) - The character before index
l
(if exists) must be different, otherwise our segment would have started earlier - The character at index
r
(if exists) must be different, because that's why we stopped extending
- It consists of only one character (we only moved
Time Complexity: O(n)
where n
is the length of the string. Each character is visited at most twice (once by l
and once by r
).
Space Complexity: O(1)
as we only use a constant amount of extra space for the pointers.
Example walkthrough with s = "aaabbb"
, k = 3
:
- Start:
l = 0
, find segment "aaa" (r
moves from 0 to 3) - Check:
3 - 0 = 3
, which equalsk
, so returntrue
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with s = "aabbbaaa"
and k = 3
:
Step 1: Start with l = 0
(pointing to first 'a')
- Set
r = 0
- Move
r
right whiles[r] == s[0]
(which is 'a') r
moves: 0 → 1 → 2 (stops at index 2 becauses[2] = 'b'
)- Segment found: "aa" (from index 0 to 1)
- Check length:
r - l = 2 - 0 = 2
≠ 3 - Move
l
tor
:l = 2
Step 2: Now l = 2
(pointing to first 'b')
- Set
r = 2
- Move
r
right whiles[r] == s[2]
(which is 'b') r
moves: 2 → 3 → 4 → 5 (stops at index 5 becauses[5] = 'a'
)- Segment found: "bbb" (from index 2 to 4)
- Check length:
r - l = 5 - 2 = 3
= 3 ✓ - Return
true
- we found a valid substring!
The substring "bbb" satisfies all conditions:
- It has exactly length 3
- It consists of only 'b' characters
- The character before it ('a' at index 1) is different from 'b'
- The character after it ('a' at index 5) is different from 'b'
Another example with s = "aaaa"
and k = 3
:
Step 1: Start with l = 0
- Set
r = 0
- Move
r
right whiles[r] == s[0]
(which is 'a') r
moves: 0 → 1 → 2 → 3 → 4 (stops at index 4, end of string)- Segment found: "aaaa" (entire string)
- Check length:
r - l = 4 - 0 = 4
≠ 3 - Move
l
tor
:l = 4
Step 2: l = 4
which is ≥ n = 4
, so exit loop
Return false
- no segment of exactly length 3 exists. Even though "aaa" appears in the string, it's part of a longer run of 'a's, so it doesn't meet the isolation requirement.
Solution Implementation
1class Solution:
2 def hasSpecialSubstring(self, s: str, k: int) -> bool:
3 """
4 Check if string s contains a substring of exactly k consecutive identical characters.
5
6 Args:
7 s: Input string to search
8 k: Target length of consecutive identical characters
9
10 Returns:
11 True if such substring exists, False otherwise
12 """
13 left = 0
14 string_length = len(s)
15
16 # Iterate through the string to find groups of consecutive identical characters
17 while left < string_length:
18 right = left
19
20 # Extend right pointer while characters are the same as the character at left
21 while right < string_length and s[right] == s[left]:
22 right += 1
23
24 # Check if current group has exactly k consecutive identical characters
25 if right - left == k:
26 return True
27
28 # Move left pointer to the start of next different character group
29 left = right
30
31 return False
32
1class Solution {
2 /**
3 * Checks if the string contains a special substring of exactly length k
4 * where all characters in the substring are the same.
5 *
6 * @param s the input string to search
7 * @param k the required length of the special substring
8 * @return true if such a substring exists, false otherwise
9 */
10 public boolean hasSpecialSubstring(String s, int k) {
11 int stringLength = s.length();
12
13 // Iterate through the string to find consecutive identical characters
14 for (int left = 0; left < stringLength;) {
15 // Start checking from the next position
16 int right = left + 1;
17
18 // Extend right pointer while characters are the same as the character at left
19 while (right < stringLength && s.charAt(right) == s.charAt(left)) {
20 right++;
21 }
22
23 // Check if we found a sequence of exactly k identical characters
24 if (right - left == k) {
25 return true;
26 }
27
28 // Move left pointer to the end of current sequence
29 left = right;
30 }
31
32 // No special substring of length k found
33 return false;
34 }
35}
36
1class Solution {
2public:
3 bool hasSpecialSubstring(string s, int k) {
4 int n = s.length();
5
6 // Iterate through the string to find consecutive character groups
7 int left = 0;
8 while (left < n) {
9 // Find the right boundary of current group of same characters
10 int right = left + 1;
11 while (right < n && s[right] == s[left]) {
12 right++;
13 }
14
15 // Check if current group has exactly k consecutive same characters
16 if (right - left == k) {
17 return true;
18 }
19
20 // Move to the next group of characters
21 left = right;
22 }
23
24 return false;
25 }
26};
27
1/**
2 * Checks if a string contains a substring of exactly k consecutive identical characters
3 * @param s - The input string to search in
4 * @param k - The exact length of consecutive identical characters to find
5 * @returns true if such a substring exists, false otherwise
6 */
7function hasSpecialSubstring(s: string, k: number): boolean {
8 const stringLength = s.length;
9
10 // Iterate through the string to find consecutive identical characters
11 for (let leftIndex = 0; leftIndex < stringLength; ) {
12 // Start checking from the next character
13 let rightIndex = leftIndex + 1;
14
15 // Extend rightIndex while characters are identical to the character at leftIndex
16 while (rightIndex < stringLength && s[rightIndex] === s[leftIndex]) {
17 rightIndex++;
18 }
19
20 // Check if we found exactly k consecutive identical characters
21 if (rightIndex - leftIndex === k) {
22 return true;
23 }
24
25 // Move leftIndex to the end of the current group of identical characters
26 leftIndex = rightIndex;
27 }
28
29 return false;
30}
31
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the string s
.
The algorithm uses two pointers l
and r
to traverse the string. The outer while
loop runs while l < n
, and the inner while
loop extends r
to find consecutive identical characters. Although there are nested loops, each character in the string is visited at most twice - once when r
passes over it and once when l
is updated to r
. The pointer l
jumps directly to r
after processing each group of identical characters, ensuring no character is processed redundantly. Therefore, the total number of operations is linear with respect to the string length.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space for the variables l
, r
, and n
. No additional data structures are created that scale with the input size. The space usage remains constant regardless of the length of the input string s
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding "Exactly k" Requirement
The Mistake: Developers often incorrectly check if a segment has at least k consecutive characters instead of exactly k. This leads to wrong answers when longer runs exist.
Incorrect Implementation:
# WRONG: This checks for "at least k" instead of "exactly k" if right - left >= k: # Bug: uses >= instead of == return True
Why It Fails:
For input s = "aaaaa"
, k = 3
:
- The incorrect code would return
True
because it finds 5 consecutive 'a's (which is ≥ 3) - But the correct answer is
False
because any substring of exactly 3 'a's has another 'a' adjacent to it
Correct Solution:
# CORRECT: Check for exactly k consecutive characters if right - left == k: return True
Pitfall 2: Attempting Manual Boundary Checks
The Mistake: Some developers try to manually check if characters before and after differ, leading to complex and error-prone code:
# WRONG: Overly complex manual checking
for i in range(len(s) - k + 1):
# Check if all k characters are the same
if all(s[i] == s[j] for j in range(i, i + k)):
# Manually check boundaries (error-prone!)
before_ok = (i == 0 or s[i-1] != s[i])
after_ok = (i + k == len(s) or s[i+k] != s[i])
if before_ok and after_ok:
return True
Why It's Problematic:
- More complex logic increases chance of bugs
- Inefficient: checks overlapping segments multiple times
- Harder to understand and maintain
Better Approach: The two-pointer method naturally handles boundaries because:
- When we identify a segment from
left
toright-1
, the boundaries are automatically correct - If there were more identical characters before
left
, our segment would have started earlier - If there were more identical characters at
right
, we would have extended further
Pitfall 3: Off-by-One Errors with Segment Length
The Mistake:
Confusion about whether right
points to the last character in the segment or the first different character:
# WRONG: Misunderstanding what right represents while right < string_length and s[right] == s[left]: right += 1 # Bug: Developer thinks segment length is right - left + 1 if right - left + 1 == k: # WRONG! return True
Why It Fails:
After the while loop, right
points to either:
- The first character that's different from
s[left]
, or string_length
if we reached the end
The segment spans from index left
to right-1
, so its length is right - left
, not right - left + 1
.
Correct Understanding:
# After the while loop: # - Segment starts at index: left # - Segment ends at index: right - 1 # - Segment length: (right - 1) - left + 1 = right - left if right - left == k: # CORRECT return True
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!